Skip to content

Commit 3d43b00

Browse files
Add getDoc() to firestore-exp (#3274)
1 parent ddbe218 commit 3d43b00

14 files changed

+651
-135
lines changed

.changeset/popular-beds-yell.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
---
3+
---

.changeset/popular-beds-yell2.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
---
3+
---

packages/firestore/.eslintrc.js

+7
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ module.exports = {
5959
rules: {
6060
'import/no-extraneous-dependencies': 'off'
6161
}
62+
},
63+
// TODO(firestoreexp): Remove this exception when app-exp is published
64+
{
65+
files: ['exp/**/*.ts'],
66+
rules: {
67+
'import/no-extraneous-dependencies': 'off'
68+
}
6269
}
6370
]
6471
};

packages/firestore/exp/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,6 @@ export interface FirestoreError {
488488

489489
declare module '@firebase/component' {
490490
interface NameServiceMapping {
491-
'firestore/lite': FirebaseFirestore;
491+
'firestore-exp': FirebaseFirestore;
492492
}
493493
}

packages/firestore/exp/index.node.ts

+50
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,37 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { version } from '../package.json';
19+
import { _registerComponent, registerVersion } from '@firebase/app-exp';
20+
import { Component, ComponentType } from '@firebase/component';
21+
import { Firestore } from './src/api/database';
22+
1823
export { FieldPath, documentId } from '../lite/src/api/field_path';
1924

25+
export {
26+
Firestore,
27+
initializeFirestore,
28+
getFirestore
29+
} from './src/api/database';
30+
31+
export { DocumentSnapshot, QueryDocumentSnapshot } from './src/api/snapshot';
32+
33+
export { SnapshotMetadata } from '../src/api/database';
34+
35+
export {
36+
DocumentReference,
37+
CollectionReference,
38+
Query,
39+
doc,
40+
collection,
41+
collectionGroup,
42+
parent
43+
} from '../lite/src/api/reference';
44+
45+
export { runTransaction, Transaction } from '../lite/src/api/transaction';
46+
47+
export { getDoc } from './src/api/reference';
48+
2049
export {
2150
FieldValue,
2251
deleteField,
@@ -33,3 +62,24 @@ export { Blob } from '../src/api/blob';
3362
export { GeoPoint } from '../src/api/geo_point';
3463

3564
export { Timestamp } from '../src/api/timestamp';
65+
66+
export { refEqual, queryEqual } from '../lite/src/api/reference';
67+
68+
export function registerFirestore(): void {
69+
_registerComponent(
70+
new Component(
71+
'firestore-exp',
72+
container => {
73+
const app = container.getProvider('app-exp').getImmediate()!;
74+
return ((app, auth) => new Firestore(app, auth))(
75+
app,
76+
container.getProvider('auth-internal')
77+
);
78+
},
79+
ComponentType.PUBLIC
80+
)
81+
);
82+
registerVersion('firestore-exp', version, 'node');
83+
}
84+
85+
registerFirestore();
+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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 * as firestore from '../../index';
19+
20+
import { _getProvider } from '@firebase/app-exp';
21+
import { FirebaseApp, _FirebaseService } from '@firebase/app-types-exp';
22+
import { Provider } from '@firebase/component';
23+
24+
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
25+
import { FirestoreClient } from '../../../src/core/firestore_client';
26+
import { AsyncQueue } from '../../../src/util/async_queue';
27+
import {
28+
ComponentProvider,
29+
MemoryComponentProvider
30+
} from '../../../src/core/component_provider';
31+
32+
import { Firestore as LiteFirestore } from '../../../lite/src/api/database';
33+
34+
/**
35+
* The root reference to the Firestore database and the entry point for the
36+
* tree-shakeable SDK.
37+
*/
38+
export class Firestore extends LiteFirestore
39+
implements firestore.FirebaseFirestore, _FirebaseService {
40+
private readonly _queue = new AsyncQueue();
41+
private readonly _persistenceKey: string;
42+
private _componentProvider: ComponentProvider = new MemoryComponentProvider();
43+
44+
// Assigned via _getFirestoreClient()
45+
private _firestoreClientPromise?: Promise<FirestoreClient>;
46+
47+
// We override the Settings property of the Lite SDK since the full Firestore
48+
// SDK supports more settings.
49+
protected _settings?: firestore.Settings;
50+
51+
constructor(
52+
app: FirebaseApp,
53+
authProvider: Provider<FirebaseAuthInternalName>
54+
) {
55+
super(app, authProvider);
56+
this._persistenceKey = app.name;
57+
}
58+
59+
_getSettings(): firestore.Settings {
60+
if (!this._settings) {
61+
this._settings = {};
62+
}
63+
return this._settings;
64+
}
65+
66+
_getFirestoreClient(): Promise<FirestoreClient> {
67+
if (!this._firestoreClientPromise) {
68+
const settings = this._getSettings();
69+
const databaseInfo = this._makeDatabaseInfo(
70+
settings.host,
71+
settings.ssl,
72+
settings.experimentalForceLongPolling
73+
);
74+
75+
const firestoreClient = new FirestoreClient(
76+
databaseInfo,
77+
this._credentials,
78+
this._queue
79+
);
80+
81+
this._firestoreClientPromise = firestoreClient
82+
.start(this._componentProvider, { durable: false })
83+
.then(() => firestoreClient);
84+
}
85+
86+
return this._firestoreClientPromise;
87+
}
88+
89+
async delete(): Promise<void> {
90+
// TODO(firestoreexp): Call terminate() once implemented
91+
}
92+
}
93+
94+
export function initializeFirestore(
95+
app: FirebaseApp,
96+
settings: firestore.Settings
97+
): Firestore {
98+
const firestore = _getProvider(
99+
app,
100+
'firestore-exp'
101+
).getImmediate() as Firestore;
102+
firestore._configureClient(settings);
103+
return firestore;
104+
}
105+
106+
export function getFirestore(app: FirebaseApp): Firestore {
107+
return _getProvider(app, 'firestore-exp').getImmediate() as Firestore;
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
// See https://github.com/typescript-eslint/typescript-eslint/issues/363
19+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
20+
import * as firestore from '../../index';
21+
22+
import { Firestore } from './database';
23+
import { DocumentKeyReference } from '../../../src/api/user_data_reader';
24+
import { debugAssert } from '../../../src/util/assert';
25+
import { cast } from '../../../lite/src/api/util';
26+
import { DocumentSnapshot } from './snapshot';
27+
import {
28+
getDocViaSnapshotListener,
29+
SnapshotMetadata
30+
} from '../../../src/api/database';
31+
import { ViewSnapshot } from '../../../src/core/view_snapshot';
32+
import { DocumentReference } from '../../../lite/src/api/reference';
33+
34+
export function getDoc<T>(
35+
reference: firestore.DocumentReference<T>
36+
): Promise<firestore.DocumentSnapshot<T>> {
37+
const ref = cast<DocumentReference<T>>(reference, DocumentReference);
38+
const firestore = cast<Firestore>(ref.firestore, Firestore);
39+
return firestore._getFirestoreClient().then(async firestoreClient => {
40+
const viewSnapshot = await getDocViaSnapshotListener(firestoreClient, ref);
41+
return convertToDocSnapshot(firestore, ref, viewSnapshot);
42+
});
43+
}
44+
45+
/**
46+
* Converts a ViewSnapshot that contains the single document specified by `ref`
47+
* to a DocumentSnapshot.
48+
*/
49+
function convertToDocSnapshot<T>(
50+
firestore: Firestore,
51+
ref: DocumentKeyReference<T>,
52+
snapshot: ViewSnapshot
53+
): DocumentSnapshot<T> {
54+
debugAssert(
55+
snapshot.docs.size <= 1,
56+
'Expected zero or a single result on a document-only query'
57+
);
58+
const doc = snapshot.docs.get(ref._key);
59+
60+
return new DocumentSnapshot(
61+
firestore,
62+
ref._key,
63+
doc,
64+
ref._converter,
65+
new SnapshotMetadata(snapshot.hasPendingWrites, snapshot.fromCache)
66+
);
67+
}
+111
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 * as firestore from '../../index';
19+
20+
import { DocumentKey } from '../../../src/model/document_key';
21+
import { Document } from '../../../src/model/document';
22+
import {
23+
ServerTimestampBehavior,
24+
UserDataWriter
25+
} from '../../../src/api/user_data_writer';
26+
import {
27+
fieldPathFromArgument,
28+
DocumentSnapshot as LiteDocumentSnapshot
29+
} from '../../../lite/src/api/snapshot';
30+
import { Firestore } from './database';
31+
import { cast } from '../../../lite/src/api/util';
32+
import { DocumentReference } from '../../../lite/src/api/reference';
33+
import { SnapshotMetadata } from '../../../src/api/database';
34+
35+
const DEFAULT_SERVER_TIMESTAMP_BEHAVIOR: ServerTimestampBehavior = 'none';
36+
37+
export class DocumentSnapshot<T = firestore.DocumentData>
38+
extends LiteDocumentSnapshot<T>
39+
implements firestore.DocumentSnapshot<T> {
40+
private readonly _firestoreImpl: Firestore;
41+
42+
constructor(
43+
readonly _firestore: Firestore,
44+
key: DocumentKey,
45+
document: Document | null,
46+
converter: firestore.FirestoreDataConverter<T> | null,
47+
readonly metadata: firestore.SnapshotMetadata
48+
) {
49+
super(_firestore, key, document, converter);
50+
this._firestoreImpl = cast(_firestore, Firestore);
51+
}
52+
53+
exists(): this is firestore.QueryDocumentSnapshot<T> {
54+
return super.exists();
55+
}
56+
57+
data(options: firestore.SnapshotOptions = {}): T | undefined {
58+
if (!this._document) {
59+
return undefined;
60+
} else if (this._converter) {
61+
// We only want to use the converter and create a new DocumentSnapshot
62+
// if a converter has been provided.
63+
const snapshot = new QueryDocumentSnapshot(
64+
this._firestore,
65+
this._key,
66+
this._document,
67+
/* converter= */ null,
68+
this.metadata
69+
);
70+
return this._converter.fromFirestore(snapshot);
71+
} else {
72+
const userDataWriter = new UserDataWriter(
73+
this._firestoreImpl._databaseId,
74+
/* timestampsInSnapshots= */ true,
75+
options.serverTimestamps || DEFAULT_SERVER_TIMESTAMP_BEHAVIOR,
76+
key =>
77+
new DocumentReference(this._firestore, key, /* converter= */ null)
78+
);
79+
return userDataWriter.convertValue(this._document.toProto()) as T;
80+
}
81+
}
82+
83+
get(
84+
fieldPath: string | firestore.FieldPath,
85+
options: firestore.SnapshotOptions = {}
86+
): unknown {
87+
if (this._document) {
88+
const value = this._document
89+
.data()
90+
.field(fieldPathFromArgument('DocumentSnapshot.get', fieldPath));
91+
if (value !== null) {
92+
const userDataWriter = new UserDataWriter(
93+
this._firestoreImpl._databaseId,
94+
/* timestampsInSnapshots= */ true,
95+
options.serverTimestamps || DEFAULT_SERVER_TIMESTAMP_BEHAVIOR,
96+
key => new DocumentReference(this._firestore, key, this._converter)
97+
);
98+
return userDataWriter.convertValue(value);
99+
}
100+
}
101+
return undefined;
102+
}
103+
}
104+
105+
export class QueryDocumentSnapshot<T = firestore.DocumentData>
106+
extends DocumentSnapshot<T>
107+
implements firestore.QueryDocumentSnapshot<T> {
108+
data(options: firestore.SnapshotOptions = {}): T {
109+
return super.data(options) as T;
110+
}
111+
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ import { expect } from 'chai';
2020
import { extractDependencies } from '../../../../../scripts/exp/extract-deps.helpers';
2121

2222
import * as dependencies from './dependencies.json';
23-
import * as pkg from '../../../package.json';
23+
import * as pkg from '../../package.json';
2424
import { forEach } from '../../../src/util/obj';
2525

2626
// TODO(firestorexp): Enable test
2727
// eslint-disable-next-line no-restricted-properties
2828
describe.skip('Dependencies', () => {
2929
forEach(dependencies, (api, { dependencies }) => {
3030
it(api, () => {
31-
return extractDependencies(api, pkg.exp).then(extractedDependencies => {
31+
return extractDependencies(api, pkg.main).then(extractedDependencies => {
3232
expect(extractedDependencies.classes).to.have.members(
3333
dependencies.classes,
3434
'for classes'

0 commit comments

Comments
 (0)