Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save schmidt-sebastian/6dd9cb25015d14bc96d742c3d27d6fc6 to your computer and use it in GitHub Desktop.
Save schmidt-sebastian/6dd9cb25015d14bc96d742c3d27d6fc6 to your computer and use it in GitHub Desktop.
diff --git a/packages/firestore/src/api/user_data_converter.ts b/packages/firestore/src/api/user_data_converter.ts
index a37f1be1..9544dbef 100644
--- a/packages/firestore/src/api/user_data_converter.ts
+++ b/packages/firestore/src/api/user_data_converter.ts
@@ -173,6 +173,7 @@ class ParseContext {
constructor(
readonly dataSource: UserDataSource,
readonly methodName: string,
+ readonly localWriteTime: Timestamp,
readonly path: FieldPath | null,
readonly arrayElement?: boolean,
fieldTransforms?: FieldTransform[],
@@ -193,6 +194,7 @@ class ParseContext {
const context = new ParseContext(
this.dataSource,
this.methodName,
+ this.localWriteTime,
childPath,
/*arrayElement=*/ false,
this.fieldTransforms,
@@ -207,6 +209,7 @@ class ParseContext {
const context = new ParseContext(
this.dataSource,
this.methodName,
+ this.localWriteTime,
childPath,
/*arrayElement=*/ false,
this.fieldTransforms,
@@ -222,6 +225,7 @@ class ParseContext {
return new ParseContext(
this.dataSource,
this.methodName,
+ this.localWriteTime,
/*path=*/ null,
/*arrayElement=*/ true,
this.fieldTransforms,
@@ -295,6 +299,7 @@ export class UserDataConverter {
const context = new ParseContext(
UserDataSource.Set,
methodName,
+ Timestamp.now(),
FieldPath.EMPTY_PATH
);
validatePlainObject('Data must be an object, but it was:', context, input);
@@ -317,6 +322,7 @@ export class UserDataConverter {
const context = new ParseContext(
UserDataSource.MergeSet,
methodName,
+ Timestamp.now(),
FieldPath.EMPTY_PATH
);
validatePlainObject('Data must be an object, but it was:', context, input);
@@ -371,6 +377,7 @@ export class UserDataConverter {
const context = new ParseContext(
UserDataSource.Update,
methodName,
+ Timestamp.now(),
FieldPath.EMPTY_PATH
);
validatePlainObject('Data must be an object, but it was:', context, input);
@@ -408,6 +415,7 @@ export class UserDataConverter {
const context = new ParseContext(
UserDataSource.Update,
methodName,
+ Timestamp.now(),
FieldPath.EMPTY_PATH
);
const keys = [fieldPathFromArgument(methodName, field)];
@@ -461,6 +469,7 @@ export class UserDataConverter {
const context = new ParseContext(
UserDataSource.Argument,
methodName,
+ Timestamp.now(),
FieldPath.EMPTY_PATH
);
const parsed = this.parseData(input, context);
@@ -601,12 +610,16 @@ export class UserDataConverter {
}
} else if (value instanceof ServerTimestampFieldValueImpl) {
context.fieldTransforms.push(
- new FieldTransform(context.path, ServerTimestampTransform.instance)
+ new FieldTransform(
+ context.path,
+ new ServerTimestampTransform(context.localWriteTime)
+ )
);
} else if (value instanceof ArrayUnionFieldValueImpl) {
const parsedElements = this.parseArrayTransformElements(
value.methodName,
- value._elements
+ value._elements,
+ context
);
const arrayUnion = new ArrayUnionTransformOperation(parsedElements);
context.fieldTransforms.push(
@@ -615,7 +628,8 @@ export class UserDataConverter {
} else if (value instanceof ArrayRemoveFieldValueImpl) {
const parsedElements = this.parseArrayTransformElements(
value.methodName,
- value._elements
+ value._elements,
+ context
);
const arrayRemove = new ArrayRemoveTransformOperation(parsedElements);
context.fieldTransforms.push(
@@ -671,17 +685,22 @@ export class UserDataConverter {
private parseArrayTransformElements(
methodName: string,
- elements: AnyJs[]
+ elements: AnyJs[],
+ oldcontext: ParseContext
): FieldValue[] {
return elements.map((element, i) => {
// Although array transforms are used with writes, the actual elements
// being unioned or removed are not considered writes since they cannot
// contain any FieldValue sentinels, etc.
+
+ // This should be .childContextForArgument or something along those lines
const context = new ParseContext(
UserDataSource.Argument,
methodName,
+ oldcontext.localWriteTime,
FieldPath.EMPTY_PATH
);
+
return this.parseData(element, context.childContextForArray(i));
});
}
diff --git a/packages/firestore/src/local/local_serializer.ts b/packages/firestore/src/local/local_serializer.ts
index 5fa4d850..e1d8476e 100644
--- a/packages/firestore/src/local/local_serializer.ts
+++ b/packages/firestore/src/local/local_serializer.ts
@@ -83,10 +83,10 @@ export class LocalSerializer {
/** Decodes a DbMutationBatch into a MutationBatch */
fromDbMutationBatch(dbBatch: DbMutationBatch): MutationBatch {
+ const timestamp = Timestamp.fromMillis(dbBatch.localWriteTimeMs);
const mutations = dbBatch.mutations.map(m =>
- this.remoteSerializer.fromMutation(m)
+ this.remoteSerializer.fromMutation(m, timestamp)
);
- const timestamp = Timestamp.fromMillis(dbBatch.localWriteTimeMs);
return new MutationBatch(dbBatch.batchId, timestamp, mutations);
}
diff --git a/packages/firestore/src/model/mutation.ts b/packages/firestore/src/model/mutation.ts
index 4d6e4d42..da2f41df 100644
--- a/packages/firestore/src/model/mutation.ts
+++ b/packages/firestore/src/model/mutation.ts
@@ -243,8 +243,7 @@ export abstract class Mutation {
*/
abstract applyToLocalView(
maybeDoc: MaybeDocument | null,
- baseDoc: MaybeDocument | null,
- localWriteTime: Timestamp
+ baseDoc: MaybeDocument | null
): MaybeDocument | null;
abstract isEqual(other: Mutation): boolean;
@@ -313,8 +312,7 @@ export class SetMutation extends Mutation {
applyToLocalView(
maybeDoc: MaybeDocument | null,
- baseDoc: MaybeDocument | null,
- localWriteTime: Timestamp
+ baseDoc: MaybeDocument | null
): MaybeDocument | null {
this.verifyKeyMatches(maybeDoc);
@@ -393,8 +391,7 @@ export class PatchMutation extends Mutation {
applyToLocalView(
maybeDoc: MaybeDocument | null,
- baseDoc: MaybeDocument | null,
- localWriteTime: Timestamp
+ baseDoc: MaybeDocument | null
): MaybeDocument | null {
this.verifyKeyMatches(maybeDoc);
@@ -504,8 +501,7 @@ export class TransformMutation extends Mutation {
applyToLocalView(
maybeDoc: MaybeDocument | null,
- baseDoc: MaybeDocument | null,
- localWriteTime: Timestamp
+ baseDoc: MaybeDocument | null
): MaybeDocument | null {
this.verifyKeyMatches(maybeDoc);
@@ -514,10 +510,7 @@ export class TransformMutation extends Mutation {
}
const doc = this.requireDocument(maybeDoc);
- const transformResults = this.localTransformResults(
- localWriteTime,
- baseDoc
- );
+ const transformResults = this.localTransformResults(baseDoc);
const newData = this.transformObject(doc.data, transformResults);
return new Document(this.key, doc.version, newData, {
hasLocalMutations: true
@@ -606,10 +599,7 @@ export class TransformMutation extends Mutation {
* @param baseDoc The document prior to applying this mutation batch.
* @return The transform results list.
*/
- private localTransformResults(
- localWriteTime: Timestamp,
- baseDoc: MaybeDocument | null
- ): FieldValue[] {
+ private localTransformResults(baseDoc: MaybeDocument | null): FieldValue[] {
const transformResults = [] as FieldValue[];
for (const fieldTransform of this.fieldTransforms) {
const transform = fieldTransform.transform;
@@ -619,7 +609,7 @@ export class TransformMutation extends Mutation {
previousValue = baseDoc.field(fieldTransform.field) || null;
}
- transformResults.push(transform.transform(previousValue, localWriteTime));
+ transformResults.push(transform.transform(previousValue));
}
return transformResults;
}
@@ -670,8 +660,7 @@ export class DeleteMutation extends Mutation {
applyToLocalView(
maybeDoc: MaybeDocument | null,
- baseDoc: MaybeDocument | null,
- localWriteTime: Timestamp
+ baseDoc: MaybeDocument | null
): MaybeDocument | null {
this.verifyKeyMatches(maybeDoc);
diff --git a/packages/firestore/src/model/mutation_batch.ts b/packages/firestore/src/model/mutation_batch.ts
index c5fc7661..69bc449a 100644
--- a/packages/firestore/src/model/mutation_batch.ts
+++ b/packages/firestore/src/model/mutation_batch.ts
@@ -104,11 +104,7 @@ export class MutationBatch {
for (let i = 0; i < this.mutations.length; i++) {
const mutation = this.mutations[i];
if (mutation.key.isEqual(docKey)) {
- maybeDoc = mutation.applyToLocalView(
- maybeDoc,
- baseDoc,
- this.localWriteTime
- );
+ maybeDoc = mutation.applyToLocalView(maybeDoc, baseDoc);
}
}
return maybeDoc;
diff --git a/packages/firestore/src/model/transform_operation.ts b/packages/firestore/src/model/transform_operation.ts
index 14f009ba..ab1e2034 100644
--- a/packages/firestore/src/model/transform_operation.ts
+++ b/packages/firestore/src/model/transform_operation.ts
@@ -22,22 +22,17 @@ import { assert } from '../util/assert';
/** Represents a transform within a TransformMutation. */
export interface TransformOperation {
/** Transforms the provided `previousValue`. */
- transform(previousValue: FieldValue, localWriteTime?: Timestamp): FieldValue;
+ transform(previousValue: FieldValue): FieldValue;
isEqual(other: TransformOperation): boolean;
}
/** Transforms a value into a server-generated timestamp. */
export class ServerTimestampTransform implements TransformOperation {
- private constructor() {}
- static instance = new ServerTimestampTransform();
+ constructor(private localWriteTime: Timestamp) {}
- transform(previousValue: FieldValue, localWriteTime?: Timestamp): FieldValue {
- assert(
- localWriteTime !== undefined,
- 'ServerTimestampTransform.transform() requires localWriteTime.'
- );
- return new ServerTimestampValue(localWriteTime!, previousValue);
+ transform(previousValue: FieldValue): FieldValue {
+ return new ServerTimestampValue(this.localWriteTime, previousValue);
}
isEqual(other: TransformOperation): boolean {
@@ -49,7 +44,7 @@ export class ServerTimestampTransform implements TransformOperation {
export class ArrayUnionTransformOperation implements TransformOperation {
constructor(readonly elements: FieldValue[]) {}
- transform(previousValue: FieldValue, localWriteTime?: Timestamp): FieldValue {
+ transform(previousValue: FieldValue): FieldValue {
const result = coercedFieldValuesArray(previousValue);
for (const toUnion of this.elements) {
if (!result.find(element => element.isEqual(toUnion))) {
@@ -71,7 +66,7 @@ export class ArrayUnionTransformOperation implements TransformOperation {
export class ArrayRemoveTransformOperation implements TransformOperation {
constructor(readonly elements: FieldValue[]) {}
- transform(previousValue: FieldValue, localWriteTime?: Timestamp): FieldValue {
+ transform(previousValue: FieldValue): FieldValue {
let result = coercedFieldValuesArray(previousValue);
for (const toRemove of this.elements) {
result = result.filter(element => !element.isEqual(toRemove));
diff --git a/packages/firestore/src/remote/serializer.ts b/packages/firestore/src/remote/serializer.ts
index 42915e45..eab52f38 100644
--- a/packages/firestore/src/remote/serializer.ts
+++ b/packages/firestore/src/remote/serializer.ts
@@ -836,7 +836,7 @@ export class JsonProtoSerializer {
return result;
}
- fromMutation(proto: api.Write): Mutation {
+ fromMutation(proto: api.Write, writeTime: Timestamp): Mutation {
const precondition = proto.currentDocument
? this.fromPrecondition(proto.currentDocument)
: Precondition.NONE;
@@ -857,7 +857,7 @@ export class JsonProtoSerializer {
} else if (proto.transform) {
const key = this.fromName(proto.transform.document!);
const fieldTransforms = proto.transform.fieldTransforms!.map(transform =>
- this.fromFieldTransform(transform)
+ this.fromFieldTransform(transform, writeTime)
);
assert(
precondition.exists === true,
@@ -936,7 +936,10 @@ export class JsonProtoSerializer {
}
}
- private fromFieldTransform(proto: api.FieldTransform): FieldTransform {
+ private fromFieldTransform(
+ proto: api.FieldTransform,
+ writeTime: Timestamp
+ ): FieldTransform {
// tslint:disable-next-line:no-any We need to match generated Proto types.
const type = (proto as any)['transform_type'];
let transform: TransformOperation | null = null;
@@ -945,7 +948,7 @@ export class JsonProtoSerializer {
proto.setToServerValue === 'REQUEST_TIME',
'Unknown server value transform proto: ' + JSON.stringify(proto)
);
- transform = ServerTimestampTransform.instance;
+ transform = new ServerTimestampTransform(writeTime);
} else if (hasTag(proto, type, 'appendMissingElements')) {
const values = proto.appendMissingElements!.values || [];
transform = new ArrayUnionTransformOperation(
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment