Skip to content

Commit 1b0230e

Browse files
Add DocumentSnapshot (#3130)
1 parent 3c61f24 commit 1b0230e

File tree

4 files changed

+203
-1
lines changed

4 files changed

+203
-1
lines changed

packages/firestore/lite/index.node.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export {
4747
serverTimestamp
4848
} from './src/api/field_value';
4949

50+
export { DocumentSnapshot, QueryDocumentSnapshot } from './src/api/snapshot';
51+
5052
export { setLogLevel } from '../src/util/log';
5153

5254
export function registerFirestore(): void {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 { cast } from './util';
21+
import {
22+
DOCUMENT_KEY_NAME,
23+
FieldPath as InternalFieldPath
24+
} from '../../../src/model/path';
25+
import { validateNamedArrayAtLeastNumberOfElements } from '../../../src/util/input_validation';
26+
import { Code, FirestoreError } from '../../../src/util/error';
27+
28+
/**
29+
* A FieldPath refers to a field in a document. The path may consist of a single
30+
* field name (referring to a top-level field in the document), or a list of
31+
* field names (referring to a nested field in the document).
32+
*/
33+
export class FieldPath implements firestore.FieldPath {
34+
// Note: This class is stripped down a copy of the FieldPath class in the
35+
// legacy SDK. The changes are:
36+
// - The `documentId()` static method has been removed
37+
// - Input validation is limited to errors that cannot be caught by the
38+
// TypeScript transpiler.
39+
40+
/** Internal representation of a Firestore field path. */
41+
_internalPath: InternalFieldPath;
42+
43+
/**
44+
* Creates a FieldPath from the provided field names. If more than one field
45+
* name is provided, the path will point to a nested field in a document.
46+
*
47+
* @param fieldNames A list of field names.
48+
*/
49+
constructor(...fieldNames: string[]) {
50+
validateNamedArrayAtLeastNumberOfElements(
51+
'FieldPath',
52+
fieldNames,
53+
'fieldNames',
54+
1
55+
);
56+
57+
const emptyElement = fieldNames.indexOf('');
58+
if (emptyElement !== -1) {
59+
throw new FirestoreError(
60+
Code.INVALID_ARGUMENT,
61+
`Invalid field name at argument $(i + 1). ` +
62+
'Field names must not be empty.'
63+
);
64+
}
65+
66+
this._internalPath = new InternalFieldPath(fieldNames);
67+
}
68+
69+
isEqual(other: firestore.FieldPath): boolean {
70+
const path = cast(other, FieldPath);
71+
return this._internalPath.isEqual(path._internalPath);
72+
}
73+
}
74+
75+
export function documentId(): firestore.FieldPath {
76+
return new FieldPath(DOCUMENT_KEY_NAME);
77+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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 { Firestore } from './database';
21+
import { DocumentReference } from './reference';
22+
import { FieldPath } from './field_path';
23+
import { cast } from './util';
24+
import { DocumentKey } from '../../../src/model/document_key';
25+
import { Document } from '../../../src/model/document';
26+
import { UserDataWriter } from '../../../src/api/user_data_writer';
27+
import { FieldPath as InternalFieldPath } from '../../../src/model/path';
28+
import { fieldPathFromDotSeparatedString } from '../../../src/api/user_data_reader';
29+
30+
export class DocumentSnapshot<T = firestore.DocumentData>
31+
implements firestore.DocumentSnapshot<T> {
32+
// Note: This class is stripped down version of the DocumentSnapshot in
33+
// the legacy SDK. The changes are:
34+
// - No support for SnapshotMetadata.
35+
// - No support for SnapshotOptions.
36+
37+
constructor(
38+
private _firestore: Firestore,
39+
private _key: DocumentKey,
40+
private _document: Document | null,
41+
private _converter?: firestore.FirestoreDataConverter<T>
42+
) {}
43+
44+
get id(): string {
45+
return this._key.path.lastSegment();
46+
}
47+
48+
get ref(): firestore.DocumentReference<T> {
49+
return new DocumentReference<T>(
50+
this._firestore,
51+
this._key,
52+
this._converter
53+
);
54+
}
55+
56+
exists(): this is firestore.QueryDocumentSnapshot<T> {
57+
return this._document !== null;
58+
}
59+
60+
data(): T | undefined {
61+
if (!this._document) {
62+
return undefined;
63+
} else if (this._converter) {
64+
// We only want to use the converter and create a new DocumentSnapshot
65+
// if a converter has been provided.
66+
const snapshot = new QueryDocumentSnapshot(
67+
this._firestore,
68+
this._key,
69+
this._document
70+
);
71+
return this._converter.fromFirestore(snapshot);
72+
} else {
73+
const userDataWriter = new UserDataWriter(
74+
this._firestore._databaseId,
75+
/* timestampsInSnapshots= */ false,
76+
/* serverTimestampBehavior=*/ 'none',
77+
key => new DocumentReference(this._firestore, key)
78+
);
79+
return userDataWriter.convertValue(this._document.toProto()) as T;
80+
}
81+
}
82+
83+
get(fieldPath: string | firestore.FieldPath): unknown {
84+
if (this._document) {
85+
const value = this._document
86+
.data()
87+
.field(fieldPathFromArgument('DocumentSnapshot.get', fieldPath));
88+
if (value !== null) {
89+
const userDataWriter = new UserDataWriter(
90+
this._firestore._databaseId,
91+
/* timestampsInSnapshots= */ false,
92+
/* serverTimestampBehavior=*/ 'none',
93+
key => new DocumentReference(this._firestore, key, this._converter)
94+
);
95+
return userDataWriter.convertValue(value);
96+
}
97+
}
98+
return undefined;
99+
}
100+
}
101+
102+
export class QueryDocumentSnapshot<T = firestore.DocumentData>
103+
extends DocumentSnapshot<T>
104+
implements firestore.QueryDocumentSnapshot<T> {
105+
data(): T {
106+
return super.data() as T;
107+
}
108+
}
109+
110+
/**
111+
* Helper that calls fromDotSeparatedString() but wraps any error thrown.
112+
*/
113+
export function fieldPathFromArgument(
114+
methodName: string,
115+
arg: string | firestore.FieldPath
116+
): InternalFieldPath {
117+
if (typeof arg === 'string') {
118+
return fieldPathFromDotSeparatedString(methodName, arg);
119+
} else {
120+
const path = cast(arg, FieldPath);
121+
return path._internalPath;
122+
}
123+
}

packages/firestore/src/api/user_data_reader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ export function fieldPathFromArgument(
769769
* @param path The dot-separated string form of a field path which will be split
770770
* on dots.
771771
*/
772-
function fieldPathFromDotSeparatedString(
772+
export function fieldPathFromDotSeparatedString(
773773
methodName: string,
774774
path: string
775775
): FieldPath {

0 commit comments

Comments
 (0)