Skip to content

Commit ce9e18d

Browse files
authored
Merge 56cce03 into af0c430
2 parents af0c430 + 56cce03 commit ce9e18d

32 files changed

+2330
-135
lines changed

packages/firestore/src/core/bundle.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { Query } from './query';
19+
import { SnapshotVersion } from './snapshot_version';
20+
import {
21+
fromDocument,
22+
fromName,
23+
fromVersion,
24+
JsonProtoSerializer
25+
} from '../remote/serializer';
26+
import * as bundleProto from '../protos/firestore_bundle_proto';
27+
import * as api from '../protos/firestore_proto_api';
28+
import { DocumentKey } from '../model/document_key';
29+
import { MaybeDocument, NoDocument } from '../model/document';
30+
import { debugAssert } from '../util/assert';
31+
32+
/**
33+
* Represents a Firestore bundle saved by the SDK in its local storage.
34+
*/
35+
export interface Bundle {
36+
readonly id: string;
37+
readonly version: number;
38+
/**
39+
* Set to the snapshot version of the bundle if created by the Server SDKs.
40+
* Otherwise set to SnapshotVersion.MIN.
41+
*/
42+
readonly createTime: SnapshotVersion;
43+
}
44+
45+
/**
46+
* Represents a Query saved by the SDK in its local storage.
47+
*/
48+
export interface NamedQuery {
49+
readonly name: string;
50+
readonly query: Query;
51+
/** The time at which the results for this query were read. */
52+
readonly readTime: SnapshotVersion;
53+
}
54+
55+
/**
56+
* Represents a bundled document, including the metadata and the document
57+
* itself, if it exists.
58+
*/
59+
interface BundledDocument {
60+
metadata: bundleProto.BundledDocumentMetadata;
61+
document: api.Document | undefined;
62+
}
63+
64+
/**
65+
* An array of `BundledDocument`.
66+
*/
67+
export type BundledDocuments = BundledDocument[];
68+
69+
/**
70+
* Helper to convert objects from bundles to model objects in the SDK.
71+
*/
72+
export class BundleConverter {
73+
constructor(private serializer: JsonProtoSerializer) {}
74+
75+
toDocumentKey(name: string): DocumentKey {
76+
return fromName(this.serializer, name);
77+
}
78+
79+
/**
80+
* Converts a BundleDocument to a MaybeDocument.
81+
*/
82+
toMaybeDocument(bundledDoc: BundledDocument): MaybeDocument {
83+
if (bundledDoc.metadata.exists) {
84+
debugAssert(
85+
!!bundledDoc.document,
86+
'Document is undefined when metadata.exist is true.'
87+
);
88+
return fromDocument(this.serializer, bundledDoc.document!, false);
89+
} else {
90+
return new NoDocument(
91+
this.toDocumentKey(bundledDoc.metadata.name!),
92+
this.toSnapshotVersion(bundledDoc.metadata.readTime!)
93+
);
94+
}
95+
}
96+
97+
toSnapshotVersion(time: api.Timestamp): SnapshotVersion {
98+
return fromVersion(time);
99+
}
100+
}

packages/firestore/src/core/component_provider.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import {
5454
import { newConnectivityMonitor } from '../platform/connection';
5555
import { newSerializer } from '../platform/serializer';
5656
import { getDocument, getWindow } from '../platform/dom';
57+
import { JsonProtoSerializer } from '../remote/serializer';
5758

5859
const MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE =
5960
'You are using the memory-only build of Firestore. Persistence support is ' +
@@ -101,7 +102,10 @@ export class MemoryComponentProvider implements ComponentProvider {
101102
remoteStore!: RemoteStore;
102103
eventManager!: EventManager;
103104

105+
serializer!: JsonProtoSerializer;
106+
104107
async initialize(cfg: ComponentConfiguration): Promise<void> {
108+
this.serializer = newSerializer(cfg.databaseInfo.databaseId);
105109
this.sharedClientState = this.createSharedClientState(cfg);
106110
this.persistence = this.createPersistence(cfg);
107111
await this.persistence.start();
@@ -139,7 +143,8 @@ export class MemoryComponentProvider implements ComponentProvider {
139143
return newLocalStore(
140144
this.persistence,
141145
new IndexFreeQueryEngine(),
142-
cfg.initialUser
146+
cfg.initialUser,
147+
this.serializer
143148
);
144149
}
145150

@@ -150,7 +155,7 @@ export class MemoryComponentProvider implements ComponentProvider {
150155
MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE
151156
);
152157
}
153-
return new MemoryPersistence(MemoryEagerDelegate.factory);
158+
return new MemoryPersistence(MemoryEagerDelegate.factory, this.serializer);
154159
}
155160

156161
createRemoteStore(cfg: ComponentConfiguration): RemoteStore {
@@ -225,7 +230,8 @@ export class IndexedDbComponentProvider extends MemoryComponentProvider {
225230
return newMultiTabLocalStore(
226231
this.persistence,
227232
new IndexFreeQueryEngine(),
228-
cfg.initialUser
233+
cfg.initialUser,
234+
this.serializer
229235
);
230236
}
231237

@@ -261,7 +267,6 @@ export class IndexedDbComponentProvider extends MemoryComponentProvider {
261267
const persistenceKey = IndexedDbPersistence.buildStoragePrefix(
262268
cfg.databaseInfo
263269
);
264-
const serializer = newSerializer(cfg.databaseInfo.databaseId);
265270
return new IndexedDbPersistence(
266271
cfg.persistenceSettings.synchronizeTabs,
267272
persistenceKey,
@@ -270,7 +275,7 @@ export class IndexedDbComponentProvider extends MemoryComponentProvider {
270275
cfg.asyncQueue,
271276
getWindow(),
272277
getDocument(),
273-
serializer,
278+
this.serializer,
274279
this.sharedClientState,
275280
cfg.persistenceSettings.forceOwningTab
276281
);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { PersistenceTransaction } from './persistence';
19+
import { PersistencePromise } from './persistence_promise';
20+
import * as bundleProto from '../protos/firestore_bundle_proto';
21+
import { Bundle, NamedQuery } from '../core/bundle';
22+
23+
/**
24+
* Provides interfaces to save and read Firestore bundles.
25+
*/
26+
export interface BundleCache {
27+
/**
28+
* Gets a saved `Bundle` for a given `bundleId`, returns undefined if
29+
* no bundles are found under the given id.
30+
*/
31+
getBundleMetadata(
32+
transaction: PersistenceTransaction,
33+
bundleId: string
34+
): PersistencePromise<Bundle | undefined>;
35+
36+
/**
37+
* Saves a `BundleMetadata` from a bundle into local storage, using its id as
38+
* the persistent key.
39+
*/
40+
saveBundleMetadata(
41+
transaction: PersistenceTransaction,
42+
metadata: bundleProto.BundleMetadata
43+
): PersistencePromise<void>;
44+
45+
/**
46+
* Gets a saved `NamedQuery` for the given query name. Returns undefined if
47+
* no queries are found under the given name.
48+
*/
49+
getNamedQuery(
50+
transaction: PersistenceTransaction,
51+
queryName: string
52+
): PersistencePromise<NamedQuery | undefined>;
53+
54+
/**
55+
* Saves a `NamedQuery` from a bundle, using its name as the persistent key.
56+
*/
57+
saveNamedQuery(
58+
transaction: PersistenceTransaction,
59+
query: bundleProto.NamedQuery
60+
): PersistencePromise<void>;
61+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { PersistenceTransaction } from './persistence';
19+
import { PersistencePromise } from './persistence_promise';
20+
import * as bundleProto from '../protos/firestore_bundle_proto';
21+
import { BundleCache } from './bundle_cache';
22+
import {
23+
DbBundle,
24+
DbBundlesKey,
25+
DbNamedQuery,
26+
DbNamedQueriesKey
27+
} from './indexeddb_schema';
28+
import { SimpleDbStore } from './simple_db';
29+
import { IndexedDbPersistence } from './indexeddb_persistence';
30+
import {
31+
fromDbBundle,
32+
fromDbNamedQuery,
33+
LocalSerializer,
34+
toDbBundle,
35+
toDbNamedQuery
36+
} from './local_serializer';
37+
import { Bundle, NamedQuery } from '../core/bundle';
38+
39+
export class IndexedDbBundleCache implements BundleCache {
40+
constructor(private serializer: LocalSerializer) {}
41+
42+
getBundleMetadata(
43+
transaction: PersistenceTransaction,
44+
bundleId: string
45+
): PersistencePromise<Bundle | undefined> {
46+
return bundlesStore(transaction)
47+
.get(bundleId)
48+
.next(bundle => {
49+
if (bundle) {
50+
return fromDbBundle(this.serializer, bundle);
51+
}
52+
return undefined;
53+
});
54+
}
55+
56+
saveBundleMetadata(
57+
transaction: PersistenceTransaction,
58+
bundleMetadata: bundleProto.BundleMetadata
59+
): PersistencePromise<void> {
60+
return bundlesStore(transaction).put(
61+
toDbBundle(this.serializer, bundleMetadata)
62+
);
63+
}
64+
65+
getNamedQuery(
66+
transaction: PersistenceTransaction,
67+
queryName: string
68+
): PersistencePromise<NamedQuery | undefined> {
69+
return namedQueriesStore(transaction)
70+
.get(queryName)
71+
.next(query => {
72+
if (query) {
73+
return fromDbNamedQuery(this.serializer, query);
74+
}
75+
return undefined;
76+
});
77+
}
78+
79+
saveNamedQuery(
80+
transaction: PersistenceTransaction,
81+
query: bundleProto.NamedQuery
82+
): PersistencePromise<void> {
83+
return namedQueriesStore(transaction).put(
84+
toDbNamedQuery(this.serializer, query)
85+
);
86+
}
87+
}
88+
89+
/**
90+
* Helper to get a typed SimpleDbStore for the bundles object store.
91+
*/
92+
function bundlesStore(
93+
txn: PersistenceTransaction
94+
): SimpleDbStore<DbBundlesKey, DbBundle> {
95+
return IndexedDbPersistence.getStore<DbBundlesKey, DbBundle>(
96+
txn,
97+
DbBundle.store
98+
);
99+
}
100+
101+
/**
102+
* Helper to get a typed SimpleDbStore for the namedQueries object store.
103+
*/
104+
function namedQueriesStore(
105+
txn: PersistenceTransaction
106+
): SimpleDbStore<DbNamedQueriesKey, DbNamedQuery> {
107+
return IndexedDbPersistence.getStore<DbNamedQueriesKey, DbNamedQuery>(
108+
txn,
109+
DbNamedQuery.store
110+
);
111+
}

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
EncodedResourcePath,
3131
encodeResourcePath
3232
} from './encoded_resource_path';
33+
import { IndexedDbBundleCache } from './indexeddb_bundle_cache';
3334
import { IndexedDbIndexManager } from './indexeddb_index_manager';
3435
import {
3536
IndexedDbMutationQueue,
@@ -224,6 +225,7 @@ export class IndexedDbPersistence implements Persistence {
224225
private readonly targetCache: IndexedDbTargetCache;
225226
private readonly indexManager: IndexedDbIndexManager;
226227
private readonly remoteDocumentCache: IndexedDbRemoteDocumentCache;
228+
private readonly bundleCache: IndexedDbBundleCache;
227229
private readonly webStorage: Storage | null;
228230
readonly referenceDelegate: IndexedDbLruDelegate;
229231

@@ -268,6 +270,7 @@ export class IndexedDbPersistence implements Persistence {
268270
this.serializer,
269271
this.indexManager
270272
);
273+
this.bundleCache = new IndexedDbBundleCache(this.serializer);
271274
if (this.window && this.window.localStorage) {
272275
this.webStorage = this.window.localStorage;
273276
} else {
@@ -782,6 +785,14 @@ export class IndexedDbPersistence implements Persistence {
782785
return this.indexManager;
783786
}
784787

788+
getBundleCache(): IndexedDbBundleCache {
789+
debugAssert(
790+
this.started,
791+
'Cannot initialize BundleCache before persistence is started.'
792+
);
793+
return this.bundleCache;
794+
}
795+
785796
runTransaction<T>(
786797
action: string,
787798
mode: PersistenceTransactionMode,

0 commit comments

Comments
 (0)