@@ -207,7 +207,7 @@ export class JsonProtoSerializer {
207
207
*/
208
208
private toTimestamp ( timestamp : Timestamp ) : string {
209
209
return {
210
- seconds : timestamp . seconds ,
210
+ seconds : '' + timestamp . seconds ,
211
211
nanos : timestamp . nanoseconds
212
212
// tslint:disable-next-line:no-any
213
213
} as any ;
@@ -284,6 +284,10 @@ export class JsonProtoSerializer {
284
284
) ;
285
285
return Blob . fromBase64String ( blob ) ;
286
286
} else {
287
+ assert (
288
+ ! this . options . useProto3Json ,
289
+ 'Expected bytes to be passed in as Uint8Array, but got a string instead.'
290
+ ) ;
287
291
return Blob . fromUint8Array ( blob ) ;
288
292
}
289
293
}
@@ -444,15 +448,13 @@ export class JsonProtoSerializer {
444
448
}
445
449
446
450
fromValue ( obj : api . Value ) : fieldValue . FieldValue {
447
- // tslint:disable-next-line:no-any
448
- const type = ( obj as any ) [ 'value_type' ] ;
449
- if ( hasTag ( obj , type , 'nullValue' ) ) {
451
+ if ( 'nullValue' in obj ) {
450
452
return fieldValue . NullValue . INSTANCE ;
451
- } else if ( hasTag ( obj , type , 'booleanValue' ) ) {
453
+ } else if ( 'booleanValue' in obj ) {
452
454
return fieldValue . BooleanValue . of ( obj . booleanValue ! ) ;
453
- } else if ( hasTag ( obj , type , 'integerValue' ) ) {
455
+ } else if ( 'integerValue' in obj ) {
454
456
return new fieldValue . IntegerValue ( parseInt64 ( obj . integerValue ! ) ) ;
455
- } else if ( hasTag ( obj , type , 'doubleValue' ) ) {
457
+ } else if ( 'doubleValue' in obj ) {
456
458
if ( this . options . useProto3Json ) {
457
459
// Proto 3 uses the string values 'NaN' and 'Infinity'.
458
460
if ( ( obj . doubleValue as { } ) === 'NaN' ) {
@@ -465,30 +467,30 @@ export class JsonProtoSerializer {
465
467
}
466
468
467
469
return new fieldValue . DoubleValue ( obj . doubleValue ! ) ;
468
- } else if ( hasTag ( obj , type , 'stringValue' ) ) {
470
+ } else if ( 'stringValue' in obj ) {
469
471
return new fieldValue . StringValue ( obj . stringValue ! ) ;
470
- } else if ( hasTag ( obj , type , 'mapValue' ) ) {
472
+ } else if ( 'mapValue' in obj ) {
471
473
return this . fromFields ( obj . mapValue ! . fields || { } ) ;
472
- } else if ( hasTag ( obj , type , 'arrayValue' ) ) {
474
+ } else if ( 'arrayValue' in obj ) {
473
475
// "values" is not present if the array is empty
474
476
assertPresent ( obj . arrayValue , 'arrayValue' ) ;
475
477
const values = obj . arrayValue ! . values || [ ] ;
476
478
return new fieldValue . ArrayValue ( values . map ( v => this . fromValue ( v ) ) ) ;
477
- } else if ( hasTag ( obj , type , 'timestampValue' ) ) {
479
+ } else if ( 'timestampValue' in obj ) {
478
480
assertPresent ( obj . timestampValue , 'timestampValue' ) ;
479
481
return new fieldValue . TimestampValue (
480
482
this . fromTimestamp ( obj . timestampValue ! )
481
483
) ;
482
- } else if ( hasTag ( obj , type , 'geoPointValue' ) ) {
484
+ } else if ( 'geoPointValue' in obj ) {
483
485
assertPresent ( obj . geoPointValue , 'geoPointValue' ) ;
484
486
const latitude = obj . geoPointValue ! . latitude || 0 ;
485
487
const longitude = obj . geoPointValue ! . longitude || 0 ;
486
488
return new fieldValue . GeoPointValue ( new GeoPoint ( latitude , longitude ) ) ;
487
- } else if ( hasTag ( obj , type , 'bytesValue' ) ) {
489
+ } else if ( 'bytesValue' in obj ) {
488
490
assertPresent ( obj . bytesValue , 'bytesValue' ) ;
489
491
const blob = this . fromBlob ( obj . bytesValue ! ) ;
490
492
return new fieldValue . BlobValue ( blob ) ;
491
- } else if ( hasTag ( obj , type , 'referenceValue' ) ) {
493
+ } else if ( 'referenceValue' in obj ) {
492
494
assertPresent ( obj . referenceValue , 'referenceValue' ) ;
493
495
const resourceName = this . fromResourceName ( obj . referenceValue ! ) ;
494
496
const dbId = new DatabaseId ( resourceName . get ( 1 ) , resourceName . get ( 3 ) ) ;
@@ -596,11 +598,9 @@ export class JsonProtoSerializer {
596
598
}
597
599
598
600
fromMaybeDocument ( result : api . BatchGetDocumentsResponse ) : MaybeDocument {
599
- // tslint:disable-next-line:no-any
600
- const type = ( result as any ) [ 'result' ] ;
601
- if ( hasTag ( result , type , 'found' ) ) {
601
+ if ( 'found' in result ) {
602
602
return this . fromFound ( result ) ;
603
- } else if ( hasTag ( result , type , 'missing' ) ) {
603
+ } else if ( 'missing' in result ) {
604
604
return this . fromMissing ( result ) ;
605
605
}
606
606
return fail ( 'invalid batch get response: ' + JSON . stringify ( result ) ) ;
@@ -687,10 +687,8 @@ export class JsonProtoSerializer {
687
687
}
688
688
689
689
fromWatchChange ( change : api . ListenResponse ) : WatchChange {
690
- // tslint:disable-next-line:no-any
691
- const type = ( change as any ) [ 'response_type' ] ;
692
690
let watchChange : WatchChange ;
693
- if ( hasTag ( change , type , 'targetChange' ) ) {
691
+ if ( 'targetChange' in change ) {
694
692
assertPresent ( change . targetChange , 'targetChange' ) ;
695
693
// proto3 default value is unset in JSON (undefined), so use 'NO_CHANGE'
696
694
// if unset
@@ -708,7 +706,7 @@ export class JsonProtoSerializer {
708
706
resumeToken ,
709
707
cause || null
710
708
) ;
711
- } else if ( hasTag ( change , type , 'documentChange' ) ) {
709
+ } else if ( 'documentChange' in change ) {
712
710
assertPresent ( change . documentChange , 'documentChange' ) ;
713
711
assertPresent ( change . documentChange ! . document , 'documentChange.name' ) ;
714
712
assertPresent (
@@ -740,7 +738,7 @@ export class JsonProtoSerializer {
740
738
doc . key ,
741
739
doc
742
740
) ;
743
- } else if ( hasTag ( change , type , 'documentDelete' ) ) {
741
+ } else if ( 'documentDelete' in change ) {
744
742
assertPresent ( change . documentDelete , 'documentDelete' ) ;
745
743
assertPresent ( change . documentDelete ! . document , 'documentDelete.document' ) ;
746
744
const docDelete = change . documentDelete ! ;
@@ -751,14 +749,14 @@ export class JsonProtoSerializer {
751
749
const doc = new NoDocument ( key , version ) ;
752
750
const removedTargetIds = docDelete . removedTargetIds || [ ] ;
753
751
watchChange = new DocumentWatchChange ( [ ] , removedTargetIds , doc . key , doc ) ;
754
- } else if ( hasTag ( change , type , 'documentRemove' ) ) {
752
+ } else if ( 'documentRemove' in change ) {
755
753
assertPresent ( change . documentRemove , 'documentRemove' ) ;
756
754
assertPresent ( change . documentRemove ! . document , 'documentRemove' ) ;
757
755
const docRemove = change . documentRemove ! ;
758
756
const key = this . fromName ( docRemove . document ! ) ;
759
757
const removedTargetIds = docRemove . removedTargetIds || [ ] ;
760
758
watchChange = new DocumentWatchChange ( [ ] , removedTargetIds , key , null ) ;
761
- } else if ( hasTag ( change , type , 'filter' ) ) {
759
+ } else if ( 'filter' in change ) {
762
760
// TODO(dimond): implement existence filter parsing with strategy.
763
761
assertPresent ( change . filter , 'filter' ) ;
764
762
assertPresent ( change . filter ! . targetId , 'filter.targetId' ) ;
@@ -795,9 +793,7 @@ export class JsonProtoSerializer {
795
793
// We have only reached a consistent snapshot for the entire stream if there
796
794
// is a read_time set and it applies to all targets (i.e. the list of
797
795
// targets is empty). The backend is guaranteed to send such responses.
798
- // tslint:disable-next-line:no-any
799
- const type = ( change as any ) [ 'response_type' ] ;
800
- if ( ! hasTag ( change , type , 'targetChange' ) ) {
796
+ if ( ! ( 'targetChange' in change ) ) {
801
797
return SnapshotVersion . MIN ;
802
798
}
803
799
const targetChange = change . targetChange ! ;
@@ -963,26 +959,24 @@ export class JsonProtoSerializer {
963
959
}
964
960
965
961
private fromFieldTransform ( proto : api . FieldTransform ) : FieldTransform {
966
- // tslint:disable-next-line:no-any We need to match generated Proto types.
967
- const type = ( proto as any ) [ 'transform_type' ] ;
968
962
let transform : TransformOperation | null = null ;
969
- if ( hasTag ( proto , type , 'setToServerValue' ) ) {
963
+ if ( 'setToServerValue' in proto ) {
970
964
assert (
971
965
proto . setToServerValue === 'REQUEST_TIME' ,
972
966
'Unknown server value transform proto: ' + JSON . stringify ( proto )
973
967
) ;
974
968
transform = ServerTimestampTransform . instance ;
975
- } else if ( hasTag ( proto , type , 'appendMissingElements' ) ) {
969
+ } else if ( 'appendMissingElements' in proto ) {
976
970
const values = proto . appendMissingElements ! . values || [ ] ;
977
971
transform = new ArrayUnionTransformOperation (
978
972
values . map ( v => this . fromValue ( v ) )
979
973
) ;
980
- } else if ( hasTag ( proto , type , 'removeAllFromArray' ) ) {
974
+ } else if ( 'removeAllFromArray' in proto ) {
981
975
const values = proto . removeAllFromArray ! . values || [ ] ;
982
976
transform = new ArrayRemoveTransformOperation (
983
977
values . map ( v => this . fromValue ( v ) )
984
978
) ;
985
- } else if ( hasTag ( proto , type , 'increment' ) ) {
979
+ } else if ( 'increment' in proto ) {
986
980
const operand = this . fromValue ( proto . increment ! ) ;
987
981
assert (
988
982
operand instanceof NumberValue ,
@@ -1360,35 +1354,3 @@ export class JsonProtoSerializer {
1360
1354
return FieldMask . fromArray ( fields ) ;
1361
1355
}
1362
1356
}
1363
-
1364
- /**
1365
- * Checks for a specific oneof tag in a protocol buffer message.
1366
- *
1367
- * This intentionally accommodates two distinct cases:
1368
- *
1369
- * 1) Messages containing a type tag: these are the format produced by GRPC in
1370
- * return values. These may contain default-value mappings for all tags in the
1371
- * oneof but the type tag specifies which one was actually set.
1372
- *
1373
- * 2) Messages that don't contain a type tag: these are the format required by
1374
- * GRPC as inputs. If we emitted objects with type tags, ProtoBuf.js would
1375
- * choke claiming that the tags aren't fields in the Message.
1376
- *
1377
- * Allowing both formats here makes the serializer able to consume the outputs
1378
- * it produces: for all messages it supports, fromX(toX(value)) == value.
1379
- *
1380
- * Note that case 2 suffers from ambiguity: if multiple tags are present
1381
- * without a type tag then the callers are structured in such a way that the
1382
- * first invocation will win. Since we only parse in this mode when parsing
1383
- * the output of a serialize method this works, but it's not a general
1384
- * solution.
1385
- *
1386
- * Unfortunately there is no general solution here because proto3 makes it
1387
- * impossible to distinguish unset from explicitly set fields: both have the
1388
- * default value for the type. Without the type tag but multiple value tags
1389
- * it's possible to have default values for each tag in the oneof and not be
1390
- * able to know which was actually in effect.
1391
- */
1392
- function hasTag ( obj : { } , type : string , tag : string ) : boolean {
1393
- return type === tag || ( ! type && tag in obj ) ;
1394
- }
0 commit comments