Skip to content

Commit

Permalink
WIP need generic tests, but compiles and runs
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Chen committed Dec 19, 2019
1 parent 25472e1 commit e1082ce
Show file tree
Hide file tree
Showing 10 changed files with 501 additions and 295 deletions.
19 changes: 13 additions & 6 deletions dev/src/document-change.ts
Expand Up @@ -14,7 +14,10 @@
* limitations under the License.
*/

import { FirestoreDataConverter } from '@google-cloud/firestore';
import {QueryDocumentSnapshot} from './document';
import { DocumentData } from './types';
import { defaultConverter } from './watch';

export type DocumentChangeType = 'added' | 'removed' | 'modified';

Expand All @@ -24,11 +27,12 @@ export type DocumentChangeType = 'added' | 'removed' | 'modified';
*
* @class
*/
export class DocumentChange {
export class DocumentChange<T = DocumentData> {
private readonly _type: DocumentChangeType;
private readonly _document: QueryDocumentSnapshot;
private readonly _document: QueryDocumentSnapshot<T>;
private readonly _oldIndex: number;
private readonly _newIndex: number;
private readonly _converter?: FirestoreDataConverter<T>;

/**
* @hideconstructor
Expand All @@ -42,14 +46,16 @@ export class DocumentChange {
*/
constructor(
type: DocumentChangeType,
document: QueryDocumentSnapshot,
document: QueryDocumentSnapshot<T>,
oldIndex: number,
newIndex: number
newIndex: number,
converter?: FirestoreDataConverter<T>
) {
this._type = type;
this._document = document;
this._oldIndex = oldIndex;
this._newIndex = newIndex;
this._converter = converter || defaultConverter as FirestoreDataConverter<T>;
}

/**
Expand Down Expand Up @@ -169,7 +175,7 @@ export class DocumentChange {
* @param {*} other The value to compare against.
* @return true if this `DocumentChange` is equal to the provided value.
*/
isEqual(other: DocumentChange): boolean {
isEqual(other: DocumentChange<T>): boolean {
if (this === other) {
return true;
}
Expand All @@ -179,7 +185,8 @@ export class DocumentChange {
this._type === other._type &&
this._oldIndex === other._oldIndex &&
this._newIndex === other._newIndex &&
this._document.isEqual(other._document)
this._document.isEqual(other._document) &&
this._converter === other._converter
);
}
}
86 changes: 49 additions & 37 deletions dev/src/document.ts
Expand Up @@ -28,16 +28,18 @@ import {ApiMapValue, DocumentData, UpdateMap} from './types';
import {isEmpty, isObject} from './util';

import api = google.firestore.v1;
import { FirestoreDataConverter } from '@google-cloud/firestore';
import { defaultConverter } from './watch';

/**
* Returns a builder for DocumentSnapshot and QueryDocumentSnapshot instances.
* Invoke `.build()' to assemble the final snapshot.
*
* @private
*/
export class DocumentSnapshotBuilder {
export class DocumentSnapshotBuilder<T = DocumentData> {
/** The reference to the document. */
ref?: DocumentReference;
ref?: DocumentReference<T>;

/** The fields of the Firestore `Document` Protobuf backing this document. */
fieldsProto?: ApiMapValue;
Expand All @@ -51,14 +53,20 @@ export class DocumentSnapshotBuilder {
/** The time when this document was last updated. */
updateTime?: Timestamp;

private readonly _converter: FirestoreDataConverter<T>;

constructor(converter?: FirestoreDataConverter<T>) {
this._converter = converter || defaultConverter as FirestoreDataConverter<T>;
}

/**
* Builds the DocumentSnapshot.
*
* @private
* @returns Returns either a QueryDocumentSnapshot (if `fieldsProto` was
* provided) or a DocumentSnapshot.
*/
build(): QueryDocumentSnapshot | DocumentSnapshot {
build(): QueryDocumentSnapshot<T> | DocumentSnapshot<T> {
assert(
(this.fieldsProto !== undefined) === (this.createTime !== undefined),
'Create time should be set iff document exists.'
Expand All @@ -73,9 +81,10 @@ export class DocumentSnapshotBuilder {
this.fieldsProto!,
this.readTime!,
this.createTime!,
this.updateTime!
this.updateTime!,
this.ref!._converter
)
: new DocumentSnapshot(this.ref!, undefined, this.readTime!);
: new DocumentSnapshot(this.ref!, undefined, this.readTime!, undefined, undefined, this.ref!._converter);
}
}

Expand All @@ -93,13 +102,14 @@ export class DocumentSnapshotBuilder {
*
* @class
*/
export class DocumentSnapshot {
private _ref: DocumentReference;
export class DocumentSnapshot<T = DocumentData> {
private _ref: DocumentReference<T>;
private _fieldsProto: ApiMapValue | undefined;
private _serializer: Serializer;
private _readTime: Timestamp | undefined;
private _createTime: Timestamp | undefined;
private _updateTime: Timestamp | undefined;
private readonly _converter: FirestoreDataConverter<T>;

/**
* @hideconstructor
Expand All @@ -115,19 +125,21 @@ export class DocumentSnapshot {
* if the document does not exist).
*/
constructor(
ref: DocumentReference,
ref: DocumentReference<T>,
fieldsProto?: ApiMapValue,
readTime?: Timestamp,
createTime?: Timestamp,
updateTime?: Timestamp
updateTime?: Timestamp,
converter?: FirestoreDataConverter<T>
) {
this._ref = ref;
this._fieldsProto = fieldsProto;
this._serializer = ref.firestore._serializer!;
this._readTime = readTime;
this._createTime = createTime;
this._updateTime = updateTime;
}
this._converter = converter || defaultConverter as FirestoreDataConverter<T>;
}

/**
* Creates a DocumentSnapshot from an object.
Expand All @@ -137,10 +149,10 @@ export class DocumentSnapshot {
* @param obj The object to store in the DocumentSnapshot.
* @return The created DocumentSnapshot.
*/
static fromObject(
ref: DocumentReference,
static fromObject<U>(
ref: DocumentReference<U>,
obj: DocumentData
): DocumentSnapshot {
): DocumentSnapshot<U> {
const serializer = ref.firestore._serializer!;
return new DocumentSnapshot(ref, serializer.encodeFields(obj));
}
Expand All @@ -155,10 +167,10 @@ export class DocumentSnapshot {
* @param data The field/value map to expand.
* @return The created DocumentSnapshot.
*/
static fromUpdateMap(
ref: DocumentReference,
static fromUpdateMap<U>(
ref: DocumentReference<U>,
data: UpdateMap
): DocumentSnapshot {
): DocumentSnapshot<U> {
const serializer = ref.firestore._serializer!;

/**
Expand Down Expand Up @@ -269,7 +281,7 @@ export class DocumentSnapshot {
* }
* });
*/
get ref(): DocumentReference {
get ref(): DocumentReference<T> {
return this._ref;
}

Expand Down Expand Up @@ -376,9 +388,7 @@ export class DocumentSnapshot {
*/
// We deliberately use `any` in the external API to not impose type-checking
// on end users.
// tslint:disable-next-line no-any
data(): {[field: string]: any} | undefined {
// tslint:disable-line no-any
data(): T | undefined {
const fields = this._fieldsProto;

if (fields === undefined) {
Expand All @@ -389,7 +399,7 @@ export class DocumentSnapshot {
for (const prop of Object.keys(fields)) {
obj[prop] = this._serializer.decodeValue(fields[prop]);
}
return obj;
return this._converter.fromFirestore(obj);
}

/**
Expand Down Expand Up @@ -489,14 +499,15 @@ export class DocumentSnapshot {
* @return {boolean} true if this `DocumentSnapshot` is equal to the provided
* value.
*/
isEqual(other: DocumentSnapshot): boolean {
isEqual(other: DocumentSnapshot<T>): boolean {
// Since the read time is different on every document read, we explicitly
// ignore all document metadata in this comparison.
return (
this === other ||
(other instanceof DocumentSnapshot &&
this._ref.isEqual(other._ref) &&
deepEqual(this._fieldsProto, other._fieldsProto, {strict: true}))
deepEqual(this._fieldsProto, other._fieldsProto, {strict: true}) &&
this._converter === other._converter)
);
}
}
Expand All @@ -516,7 +527,7 @@ export class DocumentSnapshot {
* @class
* @extends DocumentSnapshot
*/
export class QueryDocumentSnapshot extends DocumentSnapshot {
export class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<T> {
/**
* @hideconstructor
*
Expand All @@ -528,13 +539,14 @@ export class QueryDocumentSnapshot extends DocumentSnapshot {
* @param updateTime The time when the document was last updated.
*/
constructor(
ref: DocumentReference,
ref: DocumentReference<T>,
fieldsProto: ApiMapValue,
readTime: Timestamp,
createTime: Timestamp,
updateTime: Timestamp
updateTime: Timestamp,
converter?: FirestoreDataConverter<T>
) {
super(ref, fieldsProto, readTime, createTime, updateTime);
super(ref, fieldsProto, readTime, createTime, updateTime, converter);
}

/**
Expand Down Expand Up @@ -581,7 +593,7 @@ export class QueryDocumentSnapshot extends DocumentSnapshot {
*
* @override
*
* @returns {DocumentData} An object containing all fields in the document.
* @returns {T} An object containing all fields in the document.
*
* @example
* let query = firestore.collection('col');
Expand All @@ -591,7 +603,7 @@ export class QueryDocumentSnapshot extends DocumentSnapshot {
* console.log(`Retrieved data: ${JSON.stringify(data)}`);
* });
*/
data(): DocumentData {
data(): T {
const data = super.data();
if (!data) {
throw new Error(
Expand Down Expand Up @@ -870,7 +882,7 @@ export class DocumentMask {
* @private
* @class
*/
export class DocumentTransform {
export class DocumentTransform<T = DocumentData> {
/**
* @private
* @hideconstructor
Expand All @@ -879,7 +891,7 @@ export class DocumentTransform {
* @param transforms A Map of FieldPaths to FieldTransforms.
*/
constructor(
private readonly ref: DocumentReference,
private readonly ref: DocumentReference<T>,
private readonly transforms: Map<FieldPath, FieldTransform>
) {}

Expand All @@ -891,10 +903,10 @@ export class DocumentTransform {
* @param obj The object to extract the transformations from.
* @returns The Document Transform.
*/
static fromObject(
ref: DocumentReference,
static fromObject<T>(
ref: DocumentReference<T>,
obj: DocumentData
): DocumentTransform {
): DocumentTransform<T> {
const updateMap = new Map<FieldPath, unknown>();

for (const prop of Object.keys(obj)) {
Expand All @@ -912,10 +924,10 @@ export class DocumentTransform {
* @param data The update data to extract the transformations from.
* @returns The Document Transform.
*/
static fromUpdateMap(
ref: DocumentReference,
static fromUpdateMap<T>(
ref: DocumentReference<T>,
data: UpdateMap
): DocumentTransform {
): DocumentTransform<T> {
const transforms = new Map<FieldPath, FieldTransform>();

function encode_(val: unknown, path: FieldPath, allowTransforms: boolean) {
Expand Down

0 comments on commit e1082ce

Please sign in to comment.