Skip to content

Commit 5c742e7

Browse files
Add ProtoJS types to generated proto types
1 parent 1a0b08d commit 5c742e7

File tree

6 files changed

+87
-93
lines changed

6 files changed

+87
-93
lines changed

packages/firestore/src/api/user_data_writer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ export class UserDataWriter<T> {
7878
private convertServerTimestamp(value: ServerTimestampValue): unknown {
7979
switch (this.serverTimestampBehavior) {
8080
case 'previous':
81-
return value.previousValue ? this.convertValue(value.previousValue) : null;
81+
return value.previousValue
82+
? this.convertValue(value.previousValue)
83+
: null;
8284
case 'estimate':
8385
return this.convertTimestamp(value.localWriteTime);
8486
default:

packages/firestore/src/model/proto_values.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,22 @@
1717

1818
import * as api from '../protos/firestore_proto_api';
1919

20-
import { TypeOrder } from './field_value';
21-
import { assert, fail } from '../util/assert';
22-
import { forEach, keys, size } from '../util/obj';
23-
import { ByteString } from '../util/byte_string';
20+
import {TypeOrder} from './field_value';
21+
import {assert, fail} from '../util/assert';
22+
import {forEach, keys, size} from '../util/obj';
23+
import {ByteString} from '../util/byte_string';
2424
import {
2525
numericComparator,
2626
numericEquals,
2727
primitiveComparator
2828
} from '../util/misc';
29+
import {TimestampValue} from "../remote/serializer";
2930

3031
// A RegExp matching ISO 8601 UTC timestamps with optional fraction.
3132
const ISO_TIMESTAMP_REG_EXP = new RegExp(
3233
/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.(\d+))?Z$/
3334
);
3435

35-
// Denotes the possible representations for timestamps in the Value type.
36-
type ProtoTimestampValue =
37-
| string
38-
| { seconds?: string | number; nanos?: number };
39-
4036
/** Extracts the backend's type order for the provided value. */
4137
export function typeOrder(value: api.Value): TypeOrder {
4238
if ('nullValue' in value) {
@@ -236,8 +232,8 @@ function compareNumbers(left: api.Value, right: api.Value): number {
236232
}
237233

238234
function compareTimestamps(
239-
left: ProtoTimestampValue,
240-
right: ProtoTimestampValue
235+
left: TimestampValue,
236+
right: TimestampValue
241237
): number {
242238
if (typeof left === 'string' && typeof right === 'string') {
243239
// Use string ordering for ISO 8601 timestamps, but strip the timezone
@@ -376,7 +372,7 @@ function canonifyByteString(byteString: string | Uint8Array): string {
376372
return normalizeByteString(byteString).toBase64();
377373
}
378374

379-
function canonifyTimestamp(timestamp: ProtoTimestampValue): string {
375+
function canonifyTimestamp(timestamp: TimestampValue): string {
380376
const normalizedTimestamp = normalizeTimestamp(timestamp);
381377
return `time(${normalizedTimestamp.seconds},${normalizedTimestamp.nanos})`;
382378
}
@@ -478,7 +474,7 @@ function estimateArrayByteSize(arrayValue: api.ArrayValue): number {
478474
* nanos" representation.
479475
*/
480476
export function normalizeTimestamp(
481-
date: ProtoTimestampValue
477+
date: TimestampValue
482478
): { seconds: number; nanos: number } {
483479
assert(!!date, 'Cannot normalize null or undefined timestamp.');
484480

@@ -528,10 +524,10 @@ export function normalizeNumber(value: number | string | undefined): number {
528524
}
529525

530526
/** Converts the possible Proto types for Blobs into a ByteString. */
531-
export function normalizeByteString(blob: string | Uint8Array): ByteString {
532-
if (typeof blob === 'string') {
533-
return ByteString.fromBase64String(blob);
527+
export function normalizeByteString(value: string | Uint8Array): ByteString {
528+
if (typeof value === 'string') {
529+
return ByteString.fromBase64String(value);
534530
} else {
535-
return ByteString.fromUint8Array(blob);
531+
return ByteString.fromUint8Array(value);
536532
}
537533
}

packages/firestore/src/protos/firestore_proto_api.d.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export declare namespace firestoreV1ApiClientInterfaces {
180180
name?: string;
181181
fields?: ApiClientObjectMap<Value>;
182182
createTime?: string;
183-
updateTime?: string;
183+
updateTime?: string | { seconds?: string | number; nanos?: number };
184184
}
185185
interface DocumentChange {
186186
document?: Document;
@@ -190,7 +190,7 @@ export declare namespace firestoreV1ApiClientInterfaces {
190190
interface DocumentDelete {
191191
document?: string;
192192
removedTargetIds?: number[];
193-
readTime?: string;
193+
readTime?: string | { seconds?: string | number; nanos?: number };
194194
}
195195
interface DocumentMask {
196196
fieldPaths?: string[];
@@ -290,7 +290,7 @@ export declare namespace firestoreV1ApiClientInterfaces {
290290
}
291291
interface Precondition {
292292
exists?: boolean;
293-
updateTime?: string;
293+
updateTime?: string | { seconds?: string | number; nanos?: number };
294294
}
295295
interface Projection {
296296
fields?: FieldReference[];
@@ -333,12 +333,12 @@ export declare namespace firestoreV1ApiClientInterfaces {
333333
startAt?: Cursor;
334334
endAt?: Cursor;
335335
offset?: number;
336-
limit?: number;
336+
limit?: number | { value: number };
337337
}
338338
interface Target {
339339
query?: QueryTarget;
340340
documents?: DocumentsTarget;
341-
resumeToken?: string;
341+
resumeToken?: string | Uint8Array;
342342
readTime?: string;
343343
targetId?: number;
344344
once?: boolean;
@@ -347,8 +347,8 @@ export declare namespace firestoreV1ApiClientInterfaces {
347347
targetChangeType?: TargetChangeTargetChangeType;
348348
targetIds?: number[];
349349
cause?: Status;
350-
resumeToken?: string;
351-
readTime?: string;
350+
resumeToken?: string | Uint8Array;
351+
readTime?: string | { seconds?: string | number; nanos?: number };
352352
}
353353
interface TransactionOptions {
354354
readOnly?: ReadOnly;
@@ -382,17 +382,17 @@ export declare namespace firestoreV1ApiClientInterfaces {
382382
interface WriteRequest {
383383
streamId?: string;
384384
writes?: Write[];
385-
streamToken?: string;
385+
streamToken?: string | Uint8Array;
386386
labels?: ApiClientObjectMap<string>;
387387
}
388388
interface WriteResponse {
389389
streamId?: string;
390390
streamToken?: string;
391391
writeResults?: WriteResult[];
392-
commitTime?: string;
392+
commitTime?: string | { seconds?: string | number; nanos?: number };
393393
}
394394
interface WriteResult {
395-
updateTime?: string;
395+
updateTime?: string | { seconds?: string | number; nanos?: number };
396396
transformResults?: Value[];
397397
}
398398
}

packages/firestore/src/remote/serializer.ts

Lines changed: 38 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { Blob } from '../api/blob';
19-
import { GeoPoint } from '../api/geo_point';
20-
import { Timestamp } from '../api/timestamp';
21-
import { DatabaseId } from '../core/database_info';
18+
import {Blob} from '../api/blob';
19+
import {GeoPoint} from '../api/geo_point';
20+
import {Timestamp} from '../api/timestamp';
21+
import {DatabaseId} from '../core/database_info';
2222
import {
2323
Bound,
2424
Direction,
@@ -29,12 +29,12 @@ import {
2929
OrderBy,
3030
Query
3131
} from '../core/query';
32-
import { SnapshotVersion } from '../core/snapshot_version';
33-
import { Target } from '../core/target';
34-
import { TargetId } from '../core/types';
35-
import { TargetData, TargetPurpose } from '../local/target_data';
36-
import { Document, MaybeDocument, NoDocument } from '../model/document';
37-
import { DocumentKey } from '../model/document_key';
32+
import {SnapshotVersion} from '../core/snapshot_version';
33+
import {Target} from '../core/target';
34+
import {TargetId} from '../core/types';
35+
import {TargetData, TargetPurpose} from '../local/target_data';
36+
import {Document, MaybeDocument, NoDocument} from '../model/document';
37+
import {DocumentKey} from '../model/document_key';
3838
import * as fieldValue from '../model/field_value';
3939
import {
4040
DeleteMutation,
@@ -48,13 +48,13 @@ import {
4848
TransformMutation,
4949
VerifyMutation
5050
} from '../model/mutation';
51-
import { FieldPath, ResourcePath } from '../model/path';
51+
import {FieldPath, ResourcePath} from '../model/path';
5252
import * as api from '../protos/firestore_proto_api';
53-
import { assert, fail } from '../util/assert';
54-
import { Code, FirestoreError } from '../util/error';
53+
import {assert, fail} from '../util/assert';
54+
import {Code, FirestoreError} from '../util/error';
5555
import * as obj from '../util/obj';
56-
import { ByteString } from '../util/byte_string';
57-
import * as typeUtils from '../util/types';
56+
import {ByteString} from '../util/byte_string';
57+
import {isNullOrUndefined} from '../util/types';
5858

5959
import {
6060
ArrayRemoveTransformOperation,
@@ -63,8 +63,8 @@ import {
6363
ServerTimestampTransform,
6464
TransformOperation
6565
} from '../model/transform_operation';
66-
import { ExistenceFilter } from './existence_filter';
67-
import { mapCodeFromRpcCode, mapRpcCodeFromCode } from './rpc_error';
66+
import {ExistenceFilter} from './existence_filter';
67+
import {mapCodeFromRpcCode, mapRpcCodeFromCode} from './rpc_error';
6868
import {
6969
DocumentWatchChange,
7070
ExistenceFilterChange,
@@ -99,15 +99,13 @@ const OPERATORS = (() => {
9999
})();
100100

101101
function assertPresent(value: unknown, description: string): asserts value {
102-
assert(!typeUtils.isNullOrUndefined(value), description + ' is missing');
102+
assert(!isNullOrUndefined(value), description + ' is missing');
103103
}
104104

105-
// This is a supplement to the generated proto interfaces, which fail to account
106-
// for the fact that a timestamp may be encoded as either a string OR this.
107-
interface TimestampProto {
108-
seconds?: string | number;
109-
nanos?: number;
110-
}
105+
// Denotes the possible representations for timestamps in the Value type.
106+
export type TimestampValue =
107+
| string
108+
| { seconds?: string | number; nanos?: number };
111109

112110
export interface SerializerOptions {
113111
/**
@@ -148,46 +146,33 @@ export class JsonProtoSerializer {
148146
* our generated proto interfaces say Int32Value must be. But GRPC actually
149147
* expects a { value: <number> } struct.
150148
*/
151-
private toInt32Value(val: number | null): number | null {
152-
if (this.options.useProto3Json || typeUtils.isNullOrUndefined(val)) {
149+
private toInt32Value(val: number | null): number | { value: number } | null {
150+
if (this.options.useProto3Json || isNullOrUndefined(val)) {
153151
return val;
154152
} else {
155-
// ProtobufJS requires that we wrap Int32Values.
156-
// Use any because we need to match generated Proto types.
157-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
158-
return { value: val } as any;
153+
return { value: val };
159154
}
160155
}
161156

162157
/**
163158
* Returns a number (or null) from a google.protobuf.Int32Value proto.
164-
* DO NOT USE THIS FOR ANYTHING ELSE.
165-
* This method cheats. It's typed as accepting "number" because that's what
166-
* our generated proto interfaces say Int32Value must be, but it actually
167-
* accepts { value: number } to match our serialization in toInt32Value().
168159
*/
169-
private fromInt32Value(val: number | undefined): number | null {
160+
private fromInt32Value(
161+
val: number | { value: number } | undefined
162+
): number | null {
170163
let result;
171164
if (typeof val === 'object') {
172-
// Use any because we need to match generated Proto types.
173-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
174-
result = (val as any).value;
165+
result = val.value;
175166
} else {
176-
// We accept raw numbers (without the {value: ... } wrapper) for
177-
// compatibility with legacy persisted data.
178167
result = val;
179168
}
180-
return typeUtils.isNullOrUndefined(result) ? null : result;
169+
return isNullOrUndefined(result) ? null : result;
181170
}
182171

183172
/**
184173
* Returns a value for a Date that's appropriate to put into a proto.
185-
* DO NOT USE THIS FOR ANYTHING ELSE.
186-
* This method cheats. It's typed as returning "string" because that's what
187-
* our generated proto interfaces say dates must be. But it's easier and safer
188-
* to actually return a Timestamp proto.
189174
*/
190-
private toTimestamp(timestamp: Timestamp): string {
175+
private toTimestamp(timestamp: Timestamp): TimestampValue {
191176
if (this.options.useProto3Json) {
192177
// Serialize to ISO-8601 date format, but with full nano resolution.
193178
// Since JS Date has only millis, let's only use it for the seconds and
@@ -208,25 +193,21 @@ export class JsonProtoSerializer {
208193
}
209194
}
210195

211-
private fromTimestamp(date: string | TimestampProto): Timestamp {
196+
private fromTimestamp(date: TimestampValue): Timestamp {
212197
const timestamp = normalizeTimestamp(date);
213198
return new Timestamp(timestamp.seconds, timestamp.nanos);
214199
}
215200

216201
/**
217202
* Returns a value for bytes that's appropriate to put in a proto.
218-
* DO NOT USE THIS FOR ANYTHING ELSE.
219-
* This method cheats. It's typed as returning "string" because that's what
220-
* our generated proto interfaces say bytes must be. But it should return
221-
* an Uint8Array in Node.
222203
*
223204
* Visible for testing.
224205
*/
225-
toBytes(bytes: Blob | ByteString): string {
206+
toBytes(bytes: Blob | ByteString): string | Uint8Array {
226207
if (this.options.useProto3Json) {
227208
return bytes.toBase64();
228209
} else {
229-
return (bytes.toUint8Array() as unknown) as string;
210+
return bytes.toUint8Array();
230211
}
231212
}
232213

@@ -249,11 +230,11 @@ export class JsonProtoSerializer {
249230
}
250231
}
251232

252-
toVersion(version: SnapshotVersion): string {
233+
toVersion(version: SnapshotVersion): TimestampValue {
253234
return this.toTimestamp(version.toTimestamp());
254235
}
255236

256-
fromVersion(version: string): SnapshotVersion {
237+
fromVersion(version: TimestampValue): SnapshotVersion {
257238
assert(!!version, "Trying to deserialize version that isn't set");
258239
return SnapshotVersion.fromTimestamp(this.fromTimestamp(version));
259240
}
@@ -844,7 +825,7 @@ export class JsonProtoSerializer {
844825

845826
private fromWriteResult(
846827
proto: api.WriteResult,
847-
commitTime: string
828+
commitTime: TimestampValue
848829
): MutationResult {
849830
// NOTE: Deletes don't have an updateTime.
850831
let version = proto.updateTime
@@ -871,7 +852,7 @@ export class JsonProtoSerializer {
871852

872853
fromWriteResults(
873854
protos: api.WriteResult[] | undefined,
874-
commitTime?: string
855+
commitTime?: TimestampValue
875856
): MutationResult[] {
876857
if (protos && protos.length > 0) {
877858
assert(

packages/firestore/src/util/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export const isInteger: (value: unknown) => boolean =
5454
/**
5555
* Returns whether a variable is either undefined or null.
5656
*/
57-
export function isNullOrUndefined(value: unknown): boolean {
57+
export function isNullOrUndefined(value: unknown): value is null | undefined {
5858
return value === null || value === undefined;
5959
}
6060

0 commit comments

Comments
 (0)