Skip to content

Commit 920c0e2

Browse files
Merge branch 'master' into mrschmidt/initializefirestore
2 parents 867d9c0 + 7de1a7c commit 920c0e2

17 files changed

+655
-68
lines changed

packages/firestore/exp/index.d.ts

Lines changed: 464 additions & 2 deletions
Large diffs are not rendered by default.

packages/firestore/exp/index.node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
* limitations under the License.
1616
*/
1717

18-
export { foo, bar } from './src/api/foobar';
18+
// TODO(firestorexp): Export API

packages/firestore/exp/src/api/foobar.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
* limitations under the License.
1616
*/
1717

18+
// TODO(firestorexp): Replace with actual implementation
19+
1820
export function foo(): string {
1921
return bar();
2022
}

packages/firestore/exp/test/deps/verify_dependencies.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import * as dependencies from './dependencies.json';
2323
import * as pkg from '../../../package.json';
2424
import { forEach } from '../../../src/util/obj';
2525

26-
describe('Dependencies', () => {
26+
// TODO(firestorexp): Enable test
27+
// eslint-disable-next-line no-restricted-properties
28+
describe.skip('Dependencies', () => {
2729
forEach(dependencies, (api, { dependencies }) => {
2830
it(api, () => {
2931
return extractDependencies(api, pkg.exp).then(extractedDependencies => {

packages/firestore/src/api/database.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ import {
8888
PartialObserver,
8989
Unsubscribe
9090
} from './observer';
91-
import { fieldPathFromArgument, UserDataReader } from './user_data_reader';
91+
import {
92+
DocumentKeyReference,
93+
fieldPathFromArgument,
94+
UserDataReader
95+
} from './user_data_reader';
9296
import { UserDataWriter } from './user_data_writer';
9397
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
9498
import { Provider } from '@firebase/component';
@@ -935,6 +939,7 @@ export class WriteBatch implements firestore.WriteBatch {
935939
* A reference to a particular document in a collection in the database.
936940
*/
937941
export class DocumentReference<T = firestore.DocumentData>
942+
extends DocumentKeyReference<T>
938943
implements firestore.DocumentReference<T> {
939944
private _firestoreClient: FirestoreClient;
940945

@@ -943,6 +948,7 @@ export class DocumentReference<T = firestore.DocumentData>
943948
readonly firestore: Firestore,
944949
readonly _converter?: firestore.FirestoreDataConverter<T>
945950
) {
951+
super(firestore._databaseId, _key, _converter);
946952
this._firestoreClient = this.firestore.ensureClientConfigured();
947953
}
948954

@@ -1347,10 +1353,10 @@ export class DocumentSnapshot<T = firestore.DocumentData>
13471353
return this._converter.fromFirestore(snapshot, options);
13481354
} else {
13491355
const userDataWriter = new UserDataWriter(
1350-
this._firestore,
1356+
this._firestore._databaseId,
13511357
this._firestore._areTimestampsInSnapshotsEnabled(),
1352-
options.serverTimestamps,
1353-
/* converter= */ undefined
1358+
options.serverTimestamps || 'none',
1359+
key => new DocumentReference(key, this._firestore)
13541360
);
13551361
return userDataWriter.convertValue(this._document.toProto()) as T;
13561362
}
@@ -1369,10 +1375,10 @@ export class DocumentSnapshot<T = firestore.DocumentData>
13691375
.field(fieldPathFromArgument('DocumentSnapshot.get', fieldPath));
13701376
if (value !== null) {
13711377
const userDataWriter = new UserDataWriter(
1372-
this._firestore,
1378+
this._firestore._databaseId,
13731379
this._firestore._areTimestampsInSnapshotsEnabled(),
1374-
options.serverTimestamps,
1375-
this._converter
1380+
options.serverTimestamps || 'none',
1381+
key => new DocumentReference(key, this._firestore, this._converter)
13761382
);
13771383
return userDataWriter.convertValue(value);
13781384
}
@@ -2322,12 +2328,6 @@ export class CollectionReference<T = firestore.DocumentData> extends Query<T>
23222328
1,
23232329
pathString
23242330
);
2325-
if (pathString === '') {
2326-
throw new FirestoreError(
2327-
Code.INVALID_ARGUMENT,
2328-
'Document path must be a non-empty string'
2329-
);
2330-
}
23312331
const path = ResourcePath.fromString(pathString!);
23322332
return DocumentReference.forPath<T>(
23332333
this._query.path.child(path),
@@ -2425,8 +2425,8 @@ function validateReference<T>(
24252425
methodName: string,
24262426
documentRef: firestore.DocumentReference<T>,
24272427
firestore: Firestore
2428-
): DocumentReference<T> {
2429-
if (!(documentRef instanceof DocumentReference)) {
2428+
): DocumentKeyReference<T> {
2429+
if (!(documentRef instanceof DocumentKeyReference)) {
24302430
throw invalidClassError(methodName, 'DocumentReference', 1, documentRef);
24312431
} else if (documentRef.firestore !== firestore) {
24322432
throw new FirestoreError(

packages/firestore/src/api/user_data_reader.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,23 @@ import {
4646
import { DeleteFieldValueImpl, FieldValueImpl } from './field_value';
4747
import { GeoPoint } from './geo_point';
4848
import { PlatformSupport } from '../platform/platform';
49-
import { DocumentReference } from './database';
5049

5150
const RESERVED_FIELD_REGEX = /^__.*__$/;
5251

52+
/**
53+
* A reference to a document in a Firebase project.
54+
*
55+
* This class serves as a common base class for the public DocumentReferences
56+
* exposed in the lite, full and legacy SDK.
57+
*/
58+
export class DocumentKeyReference<T> {
59+
constructor(
60+
public readonly _databaseId: DatabaseId,
61+
public readonly _key: DocumentKey,
62+
public readonly _converter?: firestore.FirestoreDataConverter<T>
63+
) {}
64+
}
65+
5366
/** The result of parsing document data (e.g. for a setData call). */
5467
export class ParsedSetData {
5568
constructor(
@@ -652,9 +665,9 @@ function parseScalarValue(
652665
};
653666
} else if (value instanceof Blob) {
654667
return { bytesValue: context.serializer.toBytes(value) };
655-
} else if (value instanceof DocumentReference) {
668+
} else if (value instanceof DocumentKeyReference) {
656669
const thisDb = context.databaseId;
657-
const otherDb = value.firestore._databaseId;
670+
const otherDb = value._databaseId;
658671
if (!otherDb.isEqual(thisDb)) {
659672
throw context.createError(
660673
'Document reference is for database ' +
@@ -665,7 +678,7 @@ function parseScalarValue(
665678
return {
666679
referenceValue: context.serializer.toResourceName(
667680
value._key.path,
668-
value.firestore._databaseId
681+
value._databaseId
669682
)
670683
};
671684
} else if (value === undefined && context.ignoreUndefinedProperties) {
@@ -693,7 +706,7 @@ function looksLikeJsonObject(input: unknown): boolean {
693706
!(input instanceof Timestamp) &&
694707
!(input instanceof GeoPoint) &&
695708
!(input instanceof Blob) &&
696-
!(input instanceof DocumentReference) &&
709+
!(input instanceof DocumentKeyReference) &&
697710
!(input instanceof FieldValueImpl)
698711
);
699712
}

packages/firestore/src/api/user_data_writer.ts

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

2020
import * as api from '../protos/firestore_proto_api';
2121

22-
import { DocumentReference, Firestore } from './database';
22+
import { DocumentKeyReference } from './user_data_reader';
2323
import { Blob } from './blob';
2424
import { GeoPoint } from './geo_point';
2525
import { Timestamp } from './timestamp';
@@ -48,12 +48,14 @@ export type ServerTimestampBehavior = 'estimate' | 'previous' | 'none';
4848
* Converts Firestore's internal types to the JavaScript types that we expose
4949
* to the user.
5050
*/
51-
export class UserDataWriter<T = firestore.DocumentData> {
51+
export class UserDataWriter {
5252
constructor(
53-
private readonly firestore: Firestore,
53+
private readonly databaseId: DatabaseId,
5454
private readonly timestampsInSnapshots: boolean,
55-
private readonly serverTimestampBehavior?: ServerTimestampBehavior,
56-
private readonly converter?: firestore.FirestoreDataConverter<T>
55+
private readonly serverTimestampBehavior: ServerTimestampBehavior,
56+
private readonly referenceFactory: (
57+
key: DocumentKey
58+
) => DocumentKeyReference<firestore.DocumentData>
5759
) {}
5860

5961
convertValue(value: api.Value): unknown {
@@ -132,7 +134,9 @@ export class UserDataWriter<T = firestore.DocumentData> {
132134
}
133135
}
134136

135-
private convertReference(name: string): DocumentReference<T> {
137+
private convertReference(
138+
name: string
139+
): DocumentKeyReference<firestore.DocumentData> {
136140
const resourcePath = ResourcePath.fromString(name);
137141
hardAssert(
138142
isValidResourceName(resourcePath),
@@ -141,18 +145,18 @@ export class UserDataWriter<T = firestore.DocumentData> {
141145
const databaseId = new DatabaseId(resourcePath.get(1), resourcePath.get(3));
142146
const key = new DocumentKey(resourcePath.popFirst(5));
143147

144-
if (!databaseId.isEqual(this.firestore._databaseId)) {
148+
if (!databaseId.isEqual(this.databaseId)) {
145149
// TODO(b/64130202): Somehow support foreign references.
146150
logError(
147151
`Document ${key} contains a document ` +
148152
`reference within a different database (` +
149153
`${databaseId.projectId}/${databaseId.database}) which is not ` +
150154
`supported. It will be treated as a reference in the current ` +
151-
`database (${this.firestore._databaseId.projectId}/${this.firestore._databaseId.database}) ` +
155+
`database (${this.databaseId.projectId}/${this.databaseId.database}) ` +
152156
`instead.`
153157
);
154158
}
155159

156-
return new DocumentReference(key, this.firestore, this.converter);
160+
return this.referenceFactory(key);
157161
}
158162
}

packages/firestore/src/core/component_provider.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,12 @@ export class MemoryComponentProvider implements ComponentProvider {
133133
}
134134

135135
createPersistence(cfg: ComponentConfiguration): Persistence {
136-
debugAssert(
137-
!cfg.persistenceSettings.durable,
138-
'Can only start memory persistence'
139-
);
136+
if (cfg.persistenceSettings.durable) {
137+
throw new FirestoreError(
138+
Code.FAILED_PRECONDITION,
139+
MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE
140+
);
141+
}
140142
return new MemoryPersistence(MemoryEagerDelegate.factory);
141143
}
142144

@@ -162,6 +164,7 @@ export class MemoryComponentProvider implements ComponentProvider {
162164
return new SyncEngine(
163165
this.localStore,
164166
this.remoteStore,
167+
cfg.datastore,
165168
this.sharedClientState,
166169
cfg.initialUser,
167170
cfg.maxConcurrentLimboResolutions
@@ -219,6 +222,7 @@ export class IndexedDbComponentProvider extends MemoryComponentProvider {
219222
const syncEngine = new MultiTabSyncEngine(
220223
this.localStore,
221224
this.remoteStore,
225+
cfg.datastore,
222226
this.sharedClientState,
223227
cfg.initialUser,
224228
cfg.maxConcurrentLimboResolutions

packages/firestore/src/core/sync_engine.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import {
7474
import { ViewSnapshot } from './view_snapshot';
7575
import { AsyncQueue, wrapInUserErrorIfRecoverable } from '../util/async_queue';
7676
import { TransactionRunner } from './transaction_runner';
77+
import { Datastore } from '../remote/datastore';
7778

7879
const LOG_TAG = 'SyncEngine';
7980

@@ -185,6 +186,7 @@ export class SyncEngine implements RemoteSyncer {
185186
constructor(
186187
protected localStore: LocalStore,
187188
protected remoteStore: RemoteStore,
189+
protected datastore: Datastore,
188190
// PORTING NOTE: Manages state synchronization in multi-tab environments.
189191
protected sharedClientState: SharedClientState,
190192
private currentUser: User,
@@ -390,7 +392,7 @@ export class SyncEngine implements RemoteSyncer {
390392
): void {
391393
new TransactionRunner<T>(
392394
asyncQueue,
393-
this.remoteStore,
395+
this.datastore,
394396
updateFunction,
395397
deferred
396398
).run();
@@ -924,13 +926,15 @@ export class MultiTabSyncEngine extends SyncEngine
924926
constructor(
925927
protected localStore: MultiTabLocalStore,
926928
remoteStore: RemoteStore,
929+
datastore: Datastore,
927930
sharedClientState: SharedClientState,
928931
currentUser: User,
929932
maxConcurrentLimboResolutions: number
930933
) {
931934
super(
932935
localStore,
933936
remoteStore,
937+
datastore,
934938
sharedClientState,
935939
currentUser,
936940
maxConcurrentLimboResolutions
@@ -1110,14 +1114,10 @@ export class MultiTabSyncEngine extends SyncEngine
11101114
const queries = this.queriesByTarget.get(targetId);
11111115

11121116
if (queries && queries.length !== 0) {
1113-
// For queries that have a local View, we need to update their state
1114-
// in LocalStore (as the resume token and the snapshot version
1117+
// For queries that have a local View, we fetch their current state
1118+
// from LocalStore (as the resume token and the snapshot version
11151119
// might have changed) and reconcile their views with the persisted
11161120
// state (the list of syncedDocuments may have gotten out of sync).
1117-
await this.localStore.releaseTarget(
1118-
targetId,
1119-
/*keepPersistedTargetData=*/ true
1120-
);
11211121
targetData = await this.localStore.allocateTarget(
11221122
queries[0].toTarget()
11231123
);
@@ -1136,7 +1136,7 @@ export class MultiTabSyncEngine extends SyncEngine
11361136
} else {
11371137
debugAssert(
11381138
transitionToPrimary,
1139-
'A secondary tab should never have an active target without an active query.'
1139+
'A secondary tab should never have an active view without an active target.'
11401140
);
11411141
// For queries that never executed on this client, we need to
11421142
// allocate the target in LocalStore and initialize a new View.

0 commit comments

Comments
 (0)