Skip to content

Commit 31ba3ba

Browse files
Add DocumentSnapshot
This will be tested when getDoc() is added.
1 parent b636d27 commit 31ba3ba

File tree

3 files changed

+197
-1
lines changed

3 files changed

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

packages/firestore/src/api/user_data_reader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ export function fieldPathFromArgument(
763763
* @param path The dot-separated string form of a field path which will be split
764764
* on dots.
765765
*/
766-
function fieldPathFromDotSeparatedString(
766+
export function fieldPathFromDotSeparatedString(
767767
methodName: string,
768768
path: string
769769
): FieldPath {

0 commit comments

Comments
 (0)