Skip to content

Commit 1a0b08d

Browse files
Introducing UserDataReader/UserDataWriter (#2713)
This is a port of firebase/firebase-android-sdk#1177 and also brings FieldValue.value() in line with other platforms
1 parent 2eeb0eb commit 1a0b08d

File tree

9 files changed

+178
-174
lines changed

9 files changed

+178
-174
lines changed

packages/firestore/src/api/database.ts

Lines changed: 34 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import * as firestore from '@firebase/firestore-types';
1919

2020
import { FirebaseApp } from '@firebase/app-types';
2121
import { FirebaseService, _FirebaseApp } from '@firebase/app-types/private';
22-
import { Blob } from './blob';
2322
import { DatabaseId, DatabaseInfo } from '../core/database_info';
2423
import { ListenOptions } from '../core/event_manager';
2524
import {
@@ -45,10 +44,7 @@ import { Document, MaybeDocument, NoDocument } from '../model/document';
4544
import { DocumentKey } from '../model/document_key';
4645
import {
4746
ArrayValue,
48-
BlobValue,
4947
FieldValue,
50-
FieldValueOptions,
51-
ObjectValue,
5248
RefValue,
5349
ServerTimestampValue
5450
} from '../model/field_value';
@@ -105,8 +101,9 @@ import {
105101
import {
106102
DocumentKeyReference,
107103
fieldPathFromArgument,
108-
UserDataConverter
109-
} from './user_data_converter';
104+
UserDataReader
105+
} from './user_data_reader';
106+
import { UserDataWriter } from './user_data_writer';
110107
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
111108
import { Provider } from '@firebase/component';
112109

@@ -310,7 +307,7 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService {
310307
// TODO(mikelehen): Use modularized initialization instead.
311308
readonly _queue = new AsyncQueue();
312309

313-
readonly _dataConverter: UserDataConverter;
310+
readonly _dataReader: UserDataReader;
314311

315312
constructor(
316313
databaseIdOrApp: FirestoreDatabase | FirebaseApp,
@@ -340,7 +337,7 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService {
340337
}
341338

342339
this._settings = new FirestoreSettings({});
343-
this._dataConverter = this.createDataConverter(this._databaseId);
340+
this._dataReader = this.createDataReader(this._databaseId);
344341
}
345342

346343
settings(settingsLiteral: firestore.Settings): void {
@@ -535,7 +532,7 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService {
535532
return this._firestoreClient.start(persistenceSettings);
536533
}
537534

538-
private createDataConverter(databaseId: DatabaseId): UserDataConverter {
535+
private createDataReader(databaseId: DatabaseId): UserDataReader {
539536
const preConverter = (value: unknown): unknown => {
540537
if (value instanceof DocumentReference) {
541538
const thisDb = databaseId;
@@ -553,7 +550,7 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService {
553550
return value;
554551
}
555552
};
556-
return new UserDataConverter(preConverter);
553+
return new UserDataReader(preConverter);
557554
}
558555

559556
private static databaseIdFromApp(app: FirebaseApp): DatabaseId {
@@ -760,12 +757,12 @@ export class Transaction implements firestore.Transaction {
760757
);
761758
const parsed =
762759
options.merge || options.mergeFields
763-
? this._firestore._dataConverter.parseMergeData(
760+
? this._firestore._dataReader.parseMergeData(
764761
functionName,
765762
convertedValue,
766763
options.mergeFields
767764
)
768-
: this._firestore._dataConverter.parseSetData(
765+
: this._firestore._dataReader.parseSetData(
769766
functionName,
770767
convertedValue
771768
);
@@ -802,7 +799,7 @@ export class Transaction implements firestore.Transaction {
802799
documentRef,
803800
this._firestore
804801
);
805-
parsed = this._firestore._dataConverter.parseUpdateVarargs(
802+
parsed = this._firestore._dataReader.parseUpdateVarargs(
806803
'Transaction.update',
807804
fieldOrUpdateData,
808805
value,
@@ -815,7 +812,7 @@ export class Transaction implements firestore.Transaction {
815812
documentRef,
816813
this._firestore
817814
);
818-
parsed = this._firestore._dataConverter.parseUpdateData(
815+
parsed = this._firestore._dataReader.parseUpdateData(
819816
'Transaction.update',
820817
fieldOrUpdateData
821818
);
@@ -863,12 +860,12 @@ export class WriteBatch implements firestore.WriteBatch {
863860
);
864861
const parsed =
865862
options.merge || options.mergeFields
866-
? this._firestore._dataConverter.parseMergeData(
863+
? this._firestore._dataReader.parseMergeData(
867864
functionName,
868865
convertedValue,
869866
options.mergeFields
870867
)
871-
: this._firestore._dataConverter.parseSetData(
868+
: this._firestore._dataReader.parseSetData(
872869
functionName,
873870
convertedValue
874871
);
@@ -909,7 +906,7 @@ export class WriteBatch implements firestore.WriteBatch {
909906
documentRef,
910907
this._firestore
911908
);
912-
parsed = this._firestore._dataConverter.parseUpdateVarargs(
909+
parsed = this._firestore._dataReader.parseUpdateVarargs(
913910
'WriteBatch.update',
914911
fieldOrUpdateData,
915912
value,
@@ -922,7 +919,7 @@ export class WriteBatch implements firestore.WriteBatch {
922919
documentRef,
923920
this._firestore
924921
);
925-
parsed = this._firestore._dataConverter.parseUpdateData(
922+
parsed = this._firestore._dataReader.parseUpdateData(
926923
'WriteBatch.update',
927924
fieldOrUpdateData
928925
);
@@ -1059,15 +1056,12 @@ export class DocumentReference<T = firestore.DocumentData>
10591056
);
10601057
const parsed =
10611058
options.merge || options.mergeFields
1062-
? this.firestore._dataConverter.parseMergeData(
1059+
? this.firestore._dataReader.parseMergeData(
10631060
functionName,
10641061
convertedValue,
10651062
options.mergeFields
10661063
)
1067-
: this.firestore._dataConverter.parseSetData(
1068-
functionName,
1069-
convertedValue
1070-
);
1064+
: this.firestore._dataReader.parseSetData(functionName, convertedValue);
10711065
return this._firestoreClient.write(
10721066
parsed.toMutations(this._key, Precondition.NONE)
10731067
);
@@ -1091,15 +1085,15 @@ export class DocumentReference<T = firestore.DocumentData>
10911085
fieldOrUpdateData instanceof ExternalFieldPath
10921086
) {
10931087
validateAtLeastNumberOfArgs('DocumentReference.update', arguments, 2);
1094-
parsed = this.firestore._dataConverter.parseUpdateVarargs(
1088+
parsed = this.firestore._dataReader.parseUpdateVarargs(
10951089
'DocumentReference.update',
10961090
fieldOrUpdateData,
10971091
value,
10981092
moreFieldsAndValues
10991093
);
11001094
} else {
11011095
validateExactNumberOfArgs('DocumentReference.update', arguments, 1);
1102-
parsed = this.firestore._dataConverter.parseUpdateData(
1096+
parsed = this.firestore._dataReader.parseUpdateData(
11031097
'DocumentReference.update',
11041098
fieldOrUpdateData
11051099
);
@@ -1385,13 +1379,13 @@ export class DocumentSnapshot<T = firestore.DocumentData>
13851379
);
13861380
return this._converter.fromFirestore(snapshot, options);
13871381
} else {
1388-
return this.toJSObject(
1389-
this._document.data(),
1390-
FieldValueOptions.fromSnapshotOptions(
1391-
options,
1392-
this._firestore._areTimestampsInSnapshotsEnabled()
1393-
)
1394-
) as T;
1382+
const userDataWriter = new UserDataWriter(
1383+
this._firestore,
1384+
this._firestore._areTimestampsInSnapshotsEnabled(),
1385+
options.serverTimestamps,
1386+
/* converter= */ undefined
1387+
);
1388+
return userDataWriter.convertValue(this._document.data()) as T;
13951389
}
13961390
}
13971391
}
@@ -1407,13 +1401,13 @@ export class DocumentSnapshot<T = firestore.DocumentData>
14071401
.data()
14081402
.field(fieldPathFromArgument('DocumentSnapshot.get', fieldPath));
14091403
if (value !== null) {
1410-
return this.toJSValue(
1411-
value,
1412-
FieldValueOptions.fromSnapshotOptions(
1413-
options,
1414-
this._firestore._areTimestampsInSnapshotsEnabled()
1415-
)
1404+
const userDataWriter = new UserDataWriter(
1405+
this._firestore,
1406+
this._firestore._areTimestampsInSnapshotsEnabled(),
1407+
options.serverTimestamps,
1408+
this._converter
14161409
);
1410+
return userDataWriter.convertValue(value);
14171411
}
14181412
}
14191413
return undefined;
@@ -1453,50 +1447,6 @@ export class DocumentSnapshot<T = firestore.DocumentData>
14531447
this._converter === other._converter
14541448
);
14551449
}
1456-
1457-
private toJSObject(
1458-
data: ObjectValue,
1459-
options: FieldValueOptions
1460-
): firestore.DocumentData {
1461-
const result: firestore.DocumentData = {};
1462-
data.forEach((key, value) => {
1463-
result[key] = this.toJSValue(value, options);
1464-
});
1465-
return result;
1466-
}
1467-
1468-
private toJSValue(value: FieldValue, options: FieldValueOptions): unknown {
1469-
if (value instanceof ObjectValue) {
1470-
return this.toJSObject(value, options);
1471-
} else if (value instanceof ArrayValue) {
1472-
return this.toJSArray(value, options);
1473-
} else if (value instanceof RefValue) {
1474-
const key = value.value(options);
1475-
const database = this._firestore.ensureClientConfigured().databaseId();
1476-
if (!value.databaseId.isEqual(database)) {
1477-
// TODO(b/64130202): Somehow support foreign references.
1478-
log.error(
1479-
`Document ${this._key.path} contains a document ` +
1480-
`reference within a different database (` +
1481-
`${value.databaseId.projectId}/${value.databaseId.database}) which is not ` +
1482-
`supported. It will be treated as a reference in the current ` +
1483-
`database (${database.projectId}/${database.database}) ` +
1484-
`instead.`
1485-
);
1486-
}
1487-
return new DocumentReference(key, this._firestore, this._converter);
1488-
} else if (value instanceof BlobValue) {
1489-
return new Blob(value.internalValue);
1490-
} else {
1491-
return value.value(options);
1492-
}
1493-
}
1494-
1495-
private toJSArray(data: ArrayValue, options: FieldValueOptions): unknown[] {
1496-
return data.internalValue.map(value => {
1497-
return this.toJSValue(value, options);
1498-
});
1499-
}
15001450
}
15011451

15021452
export class QueryDocumentSnapshot<T = firestore.DocumentData>
@@ -1570,7 +1520,7 @@ export class Query<T = firestore.DocumentData> implements firestore.Query<T> {
15701520
) {
15711521
this.validateDisjunctiveFilterElements(value, operator);
15721522
}
1573-
fieldValue = this.firestore._dataConverter.parseQueryValue(
1523+
fieldValue = this.firestore._dataReader.parseQueryValue(
15741524
'Query.where',
15751525
value,
15761526
// We only allow nested arrays for IN queries.
@@ -1882,7 +1832,7 @@ export class Query<T = firestore.DocumentData> implements firestore.Query<T> {
18821832
const key = new DocumentKey(path);
18831833
components.push(new RefValue(this.firestore._databaseId, key));
18841834
} else {
1885-
const wrapped = this.firestore._dataConverter.parseQueryValue(
1835+
const wrapped = this.firestore._dataReader.parseQueryValue(
18861836
methodName,
18871837
rawValue
18881838
);

packages/firestore/src/api/user_data_converter.ts renamed to packages/firestore/src/api/user_data_reader.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ class ParseContext {
291291
* avoiding a circular dependency between user_data_converter.ts and
292292
* database.ts
293293
* * Tests to convert test-only sentinels (e.g. '<DELETE>') into types
294-
* compatible with UserDataConverter.
294+
* compatible with UserDataReader.
295295
*
296296
* Returns the converted value (can return back the input to act as a no-op).
297297
*
@@ -312,7 +312,7 @@ export class DocumentKeyReference {
312312
* Helper for parsing raw user input (provided via the API) into internal model
313313
* classes.
314314
*/
315-
export class UserDataConverter {
315+
export class UserDataReader {
316316
constructor(private preConverter: DataPreConverter) {}
317317

318318
/** Parse document data from a non-merge set() call. */

0 commit comments

Comments
 (0)