|
16 | 16 | */
|
17 | 17 |
|
18 | 18 | import { Firestore } from './database';
|
19 |
| -import { DatabaseInfo } from '../../../src/core/database_info'; |
20 |
| -import { |
21 |
| - FirestoreClient, |
22 |
| - PersistenceSettings |
23 |
| -} from '../../../src/core/firestore_client'; |
| 19 | +import { PersistenceSettings } from '../../../src/core/firestore_client'; |
24 | 20 | import { Code, FirestoreError } from '../../../src/util/error';
|
25 | 21 | import {
|
26 | 22 | MemoryOfflineComponentProvider,
|
27 | 23 | OfflineComponentProvider,
|
28 | 24 | OnlineComponentProvider
|
29 | 25 | } from '../../../src/core/component_provider';
|
30 |
| -import { DEFAULT_HOST, DEFAULT_SSL } from '../../../lite/src/api/components'; |
| 26 | +import { LocalStore } from '../../../src/local/local_store'; |
| 27 | +import { Deferred } from '../../../src/util/promise'; |
31 | 28 | import { logDebug } from '../../../src/util/log';
|
32 |
| - |
| 29 | +import { SyncEngine } from '../../../src/core/sync_engine'; |
| 30 | +import { RemoteStore } from '../../../src/remote/remote_store'; |
| 31 | +import { Persistence } from '../../../src/local/persistence'; |
| 32 | +import { EventManager } from '../../../src/core/event_manager'; |
33 | 33 | export const LOG_TAG = 'ComponentProvider';
|
34 | 34 |
|
35 | 35 | // The components module manages the lifetime of dependencies of the Firestore
|
36 | 36 | // client. Dependencies can be lazily constructed and only one exists per
|
37 | 37 | // Firestore instance.
|
38 | 38 |
|
39 |
| -/** |
40 |
| - * An instance map that ensures only one FirestoreClient exists per Firestore |
41 |
| - * instance. |
42 |
| - */ |
43 |
| -const firestoreClientInstances = new Map<Firestore, Promise<FirestoreClient>>(); |
| 39 | +// Instance maps that ensure that only one component provider exists per |
| 40 | +// Firestore instance. |
| 41 | +const offlineComponentProviders = new Map< |
| 42 | + Firestore, |
| 43 | + Promise<OfflineComponentProvider> |
| 44 | +>(); |
| 45 | +const onlineComponentProviders = new Map< |
| 46 | + Firestore, |
| 47 | + Promise<OnlineComponentProvider> |
| 48 | +>(); |
44 | 49 |
|
45 |
| -/** |
46 |
| - * Returns the initialized and started FirestoreClient for the given Firestore |
47 |
| - * instance. If none exists, creates a new FirestoreClient with memory |
48 |
| - * persistence. Callers must invoke removeFirestoreClient() when the Firestore |
49 |
| - * instance is terminated. |
50 |
| - */ |
51 |
| -export function getFirestoreClient( |
| 50 | +export async function setOfflineComponentProvider( |
| 51 | + firestore: Firestore, |
| 52 | + persistenceSettings: PersistenceSettings, |
| 53 | + offlineComponentProvider: OfflineComponentProvider |
| 54 | +): Promise<void> { |
| 55 | + const offlineDeferred = new Deferred<OfflineComponentProvider>(); |
| 56 | + offlineComponentProviders.set(firestore, offlineDeferred.promise); |
| 57 | + |
| 58 | + const configuration = await firestore._getConfiguration(); |
| 59 | + configuration.persistenceSettings = persistenceSettings; |
| 60 | + |
| 61 | + logDebug(LOG_TAG, 'Initializing OfflineComponentProvider'); |
| 62 | + await offlineComponentProvider.initialize(configuration); |
| 63 | + firestore._setCredentialChangeListener(user => |
| 64 | + // TODO(firestorexp): This should be a retryable IndexedDB operation |
| 65 | + firestore._queue.enqueueAndForget(() => |
| 66 | + // TODO(firestorexp): Make sure handleUserChange is a no-op if user |
| 67 | + // didn't change |
| 68 | + offlineComponentProvider.localStore.handleUserChange(user) |
| 69 | + ) |
| 70 | + ); |
| 71 | + // When a user calls clearPersistence() in one client, all other clients |
| 72 | + // need to be terminated to allow the delete to succeed. |
| 73 | + offlineComponentProvider.persistence.setDatabaseDeletedListener(() => |
| 74 | + firestore.delete() |
| 75 | + ); |
| 76 | + offlineDeferred.resolve(offlineComponentProvider); |
| 77 | +} |
| 78 | + |
| 79 | +export async function setOnlineComponentProvider( |
| 80 | + firestore: Firestore, |
| 81 | + onlineComponentProvider: OnlineComponentProvider |
| 82 | +): Promise<void> { |
| 83 | + const onlineDeferred = new Deferred<OnlineComponentProvider>(); |
| 84 | + onlineComponentProviders.set(firestore, onlineDeferred.promise); |
| 85 | + |
| 86 | + const configuration = await firestore._getConfiguration(); |
| 87 | + const offlineComponentProvider = await getOfflineComponentProvider(firestore); |
| 88 | + |
| 89 | + logDebug(LOG_TAG, 'Initializing OnlineComponentProvider'); |
| 90 | + await onlineComponentProvider.initialize( |
| 91 | + offlineComponentProvider, |
| 92 | + configuration |
| 93 | + ); |
| 94 | + // The CredentialChangeListener of the online component provider takes |
| 95 | + // precedence over the offline component provider. |
| 96 | + firestore._setCredentialChangeListener(user => |
| 97 | + firestore._queue.enqueueAndForget(() => |
| 98 | + onlineComponentProvider.remoteStore.handleCredentialChange(user) |
| 99 | + ) |
| 100 | + ); |
| 101 | + onlineDeferred.resolve(onlineComponentProvider); |
| 102 | +} |
| 103 | + |
| 104 | +function getOfflineComponentProvider( |
52 | 105 | firestore: Firestore
|
53 |
| -): Promise<FirestoreClient> { |
54 |
| - if (firestore._terminated) { |
55 |
| - throw new FirestoreError( |
56 |
| - Code.FAILED_PRECONDITION, |
57 |
| - 'The client has already been terminated.' |
58 |
| - ); |
59 |
| - } |
60 |
| - if (!firestoreClientInstances.has(firestore)) { |
| 106 | +): Promise<OfflineComponentProvider> { |
| 107 | + verifyNotTerminated(firestore); |
| 108 | + |
| 109 | + if (!offlineComponentProviders.has(firestore)) { |
| 110 | + logDebug(LOG_TAG, 'Using default OfflineComponentProvider'); |
61 | 111 | // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
62 |
| - initializeFirestoreClient( |
| 112 | + setOfflineComponentProvider( |
63 | 113 | firestore,
|
64 |
| - new MemoryOfflineComponentProvider(), |
65 |
| - new OnlineComponentProvider(), |
66 |
| - { |
67 |
| - durable: false |
68 |
| - } |
| 114 | + { durable: false }, |
| 115 | + new MemoryOfflineComponentProvider() |
69 | 116 | );
|
70 | 117 | }
|
71 |
| - return firestoreClientInstances.get(firestore)!; |
| 118 | + return offlineComponentProviders.get(firestore)!; |
72 | 119 | }
|
73 | 120 |
|
74 |
| -/** |
75 |
| - * Creates a new FirestoreClient for the given Firestore instance. Throws if the |
76 |
| - * instance exists. |
77 |
| - * |
78 |
| - * @param firestore The Firestore instance for which to create the |
79 |
| - * FirestoreClient. |
80 |
| - * @param offlineComponentProvider Provider that returns all components required |
81 |
| - * for memory-only or IndexedDB persistence. |
82 |
| - * @param onlineComponentProvider Provider that returns all components required |
83 |
| - * for online support. |
84 |
| - * @param persistenceSettings Settings for the component provider. |
85 |
| - */ |
86 |
| -export function initializeFirestoreClient( |
87 |
| - firestore: Firestore, |
88 |
| - offlineComponentProvider: OfflineComponentProvider, |
89 |
| - onlineComponentProvider: OnlineComponentProvider, |
90 |
| - persistenceSettings: PersistenceSettings |
91 |
| -): Promise<void> { |
92 |
| - if (firestoreClientInstances.has(firestore)) { |
| 121 | +function getOnlineComponentProvider( |
| 122 | + firestore: Firestore |
| 123 | +): Promise<OnlineComponentProvider> { |
| 124 | + verifyNotTerminated(firestore); |
| 125 | + |
| 126 | + if (!onlineComponentProviders.has(firestore)) { |
| 127 | + logDebug(LOG_TAG, 'Using default OnlineComponentProvider'); |
| 128 | + // eslint-disable-next-line @typescript-eslint/no-floating-promises |
| 129 | + setOnlineComponentProvider(firestore, new OnlineComponentProvider()); |
| 130 | + } |
| 131 | + return onlineComponentProviders.get(firestore)!; |
| 132 | +} |
| 133 | + |
| 134 | +function verifyNotTerminated(firestore: Firestore): void { |
| 135 | + if (firestore._terminated) { |
93 | 136 | throw new FirestoreError(
|
94 | 137 | Code.FAILED_PRECONDITION,
|
95 |
| - 'Firestore has already been started and persistence can no longer ' + |
96 |
| - 'be enabled. You can only enable persistence before calling ' + |
97 |
| - 'any other methods on a Firestore object.' |
| 138 | + 'The client has already been terminated.' |
98 | 139 | );
|
99 | 140 | }
|
100 |
| - logDebug(LOG_TAG, 'Initializing FirestoreClient'); |
101 |
| - const settings = firestore._getSettings(); |
102 |
| - const databaseInfo = new DatabaseInfo( |
103 |
| - firestore._databaseId, |
104 |
| - firestore._persistenceKey, |
105 |
| - settings.host ?? DEFAULT_HOST, |
106 |
| - settings.ssl ?? DEFAULT_SSL, |
107 |
| - /** forceLongPolling= */ false |
| 141 | +} |
| 142 | + |
| 143 | +export function getSyncEngine(firestore: Firestore): Promise<SyncEngine> { |
| 144 | + return getOnlineComponentProvider(firestore).then( |
| 145 | + components => components.syncEngine |
108 | 146 | );
|
109 |
| - const firestoreClient = new FirestoreClient( |
110 |
| - firestore._credentials, |
111 |
| - firestore._queue |
| 147 | +} |
| 148 | + |
| 149 | +export function getRemoteStore(firestore: Firestore): Promise<RemoteStore> { |
| 150 | + return getOnlineComponentProvider(firestore).then( |
| 151 | + components => components.remoteStore |
112 | 152 | );
|
113 |
| - const initializationPromise = firestoreClient.start( |
114 |
| - databaseInfo, |
115 |
| - offlineComponentProvider, |
116 |
| - onlineComponentProvider, |
117 |
| - persistenceSettings |
| 153 | +} |
| 154 | + |
| 155 | +export function getEventManager(firestore: Firestore): Promise<EventManager> { |
| 156 | + return getOnlineComponentProvider(firestore).then( |
| 157 | + components => components.eventManager |
118 | 158 | );
|
119 |
| - firestoreClientInstances.set( |
120 |
| - firestore, |
121 |
| - initializationPromise.then(() => firestoreClient) |
| 159 | +} |
| 160 | + |
| 161 | +export function getPersistence(firestore: Firestore): Promise<Persistence> { |
| 162 | + return getOfflineComponentProvider(firestore).then( |
| 163 | + components => components.persistence |
122 | 164 | );
|
123 |
| - return initializationPromise; |
124 | 165 | }
|
125 | 166 |
|
126 |
| -/** |
127 |
| - * Removes and terminates the FirestoreClient for the given instance if it has |
128 |
| - * been started. |
129 |
| - */ |
130 |
| -export async function removeFirestoreClient( |
131 |
| - firestore: Firestore |
132 |
| -): Promise<void> { |
133 |
| - const firestoreClientPromise = firestoreClientInstances.get(firestore); |
134 |
| - if (firestoreClientPromise) { |
135 |
| - firestoreClientInstances.delete(firestore); |
136 |
| - return firestoreClientPromise.then(firestoreClient => |
137 |
| - firestoreClient.terminate() |
138 |
| - ); |
139 |
| - } |
| 167 | +export function getLocalStore(firestore: Firestore): Promise<LocalStore> { |
| 168 | + return getOfflineComponentProvider(firestore).then( |
| 169 | + provider => provider.localStore |
| 170 | + ); |
140 | 171 | }
|
141 | 172 |
|
142 | 173 | /**
|
143 | 174 | * Removes all components associated with the provided instance. Must be called
|
144 | 175 | * when the Firestore instance is terminated.
|
145 | 176 | */
|
146 | 177 | export async function removeComponents(firestore: Firestore): Promise<void> {
|
147 |
| - const firestoreClientPromise = firestoreClientInstances.get(firestore); |
148 |
| - if (firestoreClientPromise) { |
149 |
| - logDebug(LOG_TAG, 'Removing FirestoreClient'); |
150 |
| - firestoreClientInstances.delete(firestore); |
151 |
| - return (await firestoreClientPromise).terminate(); |
| 178 | + const onlineComponentProviderPromise = onlineComponentProviders.get( |
| 179 | + firestore |
| 180 | + ); |
| 181 | + if (onlineComponentProviderPromise) { |
| 182 | + logDebug(LOG_TAG, 'Removing OnlineComponentProvider'); |
| 183 | + onlineComponentProviders.delete(firestore); |
| 184 | + await (await onlineComponentProviderPromise).terminate(); |
| 185 | + } |
| 186 | + |
| 187 | + const offlineComponentProviderPromise = offlineComponentProviders.get( |
| 188 | + firestore |
| 189 | + ); |
| 190 | + if (offlineComponentProviderPromise) { |
| 191 | + logDebug(LOG_TAG, 'Removing OfflineComponentProvider'); |
| 192 | + offlineComponentProviders.delete(firestore); |
| 193 | + await (await offlineComponentProviderPromise).terminate(); |
152 | 194 | }
|
153 | 195 | }
|
0 commit comments