Skip to content

add support for set() with SetOptions when using FirestoreDataConverter #3254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jun 26, 2020
Merged
68 changes: 60 additions & 8 deletions packages/firebase/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
* limitations under the License.
*/

import { DocumentData } from '@firebase/firestore-types';

/**
* <code>firebase</code> is a global namespace from which all Firebase
* services are accessed.
Expand Down Expand Up @@ -7859,9 +7861,12 @@ declare namespace firebase.firestore {
/**
* Called by the Firestore SDK to convert a custom model object of type T
* into a plain Javascript object (suitable for writing directly to the
* Firestore database).
* Firestore database). To use set() with `merge` and `mergeFields,
* toFirestore() must be defined with `Partial<T>`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, suggest backticks.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. thanks for the review!

*/
toFirestore(modelObject: T): DocumentData;
toFirestore:
| ((modelObject: T) => DocumentData)
| ((modelObject: Partial<T>, options?: SetOptions) => DocumentData);

/**
* Called by the Firestore SDK to convert Firestore data into an object of
Expand All @@ -7870,7 +7875,10 @@ declare namespace firebase.firestore {
* @param snapshot A QueryDocumentSnapshot containing your data and metadata.
* @param options The SnapshotOptions from the initial call to `data()`.
*/
fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): T;
fromFirestore: (
snapshot: QueryDocumentSnapshot,
options: SnapshotOptions
) => T;
}

/**
Expand Down Expand Up @@ -8310,10 +8318,32 @@ declare namespace firebase.firestore {
*/
set<T>(
documentRef: DocumentReference<T>,
data: T,
options?: SetOptions
data: Partial<T>,
options: SetOptions
): Transaction;

/**
* Writes to the document referred to by the provided `DocumentReference`.
* If the document does not exist yet, it will be created. If you pass
* `SetOptions`, the provided data can be merged into the existing document.
*
* @param documentRef A reference to the document to be set.
* @param data An object of the fields and values for the document.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I'm misreading this, but is SetOptions now required? Do we need a param for it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are adding an overload that allows to specify a data: Partial<T> parameter that must be accompanied by SetOptions. Using the data: T overload does not require SetOptions.

* @return This `Transaction` instance. Used for chaining method calls.
*/
set<T>(documentRef: DocumentReference<T>, data: T): Transaction;

/**
* Writes to the document referred to by the provided `DocumentReference`.
* If the document does not exist yet, it will be created. If you pass
* `SetOptions`, the provided data can be merged into the existing document.
*
* @param documentRef A reference to the document to be set.
* @param data An object of the fields and values for the document.
* @param options An object to configure the set behavior.
* @return This `Transaction` instance. Used for chaining method calls.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be not needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.


/**
* Updates fields in the document referred to by the provided
* `DocumentReference`. The update will fail if applied to a document that
Expand Down Expand Up @@ -8384,10 +8414,21 @@ declare namespace firebase.firestore {
*/
set<T>(
documentRef: DocumentReference<T>,
data: T,
options?: SetOptions
data: Partial<T>,
options: SetOptions
): WriteBatch;

/**
* Writes to the document referred to by the provided `DocumentReference`.
* If the document does not exist yet, it will be created. If you pass
* `SetOptions`, the provided data can be merged into the existing document.
*
* @param documentRef A reference to the document to be set.
* @param data An object of the fields and values for the document.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
set<T>(documentRef: DocumentReference<T>, data: T): WriteBatch;

/**
* Updates fields in the document referred to by the provided
* `DocumentReference`. The update will fail if applied to a document that
Expand Down Expand Up @@ -8566,7 +8607,18 @@ declare namespace firebase.firestore {
* @return A Promise resolved once the data has been successfully written
* to the backend (Note that it won't resolve while you're offline).
*/
set(data: T, options?: SetOptions): Promise<void>;
set(data: Partial<T>, options: SetOptions): Promise<void>;

/**
* Writes to the document referred to by this `DocumentReference`. If the
* document does not yet exist, it will be created. If you pass
* `SetOptions`, the provided data can be merged into an existing document.
*
* @param data A map of the fields and values for the document.
* @return A Promise resolved once the data has been successfully written
* to the backend (Note that it won't resolve while you're offline).
*/
set(data: T): Promise<void>;

/**
* Updates fields in the document referred to by this `DocumentReference`.
Expand Down
31 changes: 25 additions & 6 deletions packages/firestore-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,14 @@ export type LogLevel =
export function setLogLevel(logLevel: LogLevel): void;

export interface FirestoreDataConverter<T> {
toFirestore(modelObject: T): DocumentData;

fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): T;
toFirestore:
| ((modelObject: T) => DocumentData)
| ((modelObject: Partial<T>, options?: SetOptions) => DocumentData);

fromFirestore: (
snapshot: QueryDocumentSnapshot,
options: SnapshotOptions
) => T;
}

export class FirebaseFirestore {
Expand Down Expand Up @@ -146,7 +151,13 @@ export class Transaction {

set<T>(
documentRef: DocumentReference<T>,
data: T,
data: Partial<T>,
options: SetOptions
): Transaction;
set<T>(documentRef: DocumentReference<T>, data: T): Transaction;
set<T>(
documentRef: DocumentReference<T>,
data: T | Partial<T>,
options?: SetOptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catch-all override should not be needed in the type file.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

): Transaction;

Expand All @@ -166,7 +177,13 @@ export class WriteBatch {

set<T>(
documentRef: DocumentReference<T>,
data: T,
data: Partial<T>,
options: SetOptions
): WriteBatch;
set<T>(documentRef: DocumentReference<T>, data: T): WriteBatch;
set<T>(
documentRef: DocumentReference<T>,
data: T | Partial<T>,
options?: SetOptions
): WriteBatch;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catch-all override should not be needed in the type file.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.


Expand Down Expand Up @@ -208,7 +225,9 @@ export class DocumentReference<T = DocumentData> {

isEqual(other: DocumentReference<T>): boolean;

set(data: T, options?: SetOptions): Promise<void>;
set(data: Partial<T>, options: SetOptions): Promise<void>;
set(data: T): Promise<void>;
set(data: T | Partial<T>, options?: SetOptions): Promise<void>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catch-all override should not be needed in the type file.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.


update(data: UpdateData): Promise<void>;
update(
Expand Down
2 changes: 2 additions & 0 deletions packages/firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Unreleased
- [fixed] Fixed an issue that may have prevented the client from connecting
to the backend immediately after a user signed in.
- [fixed] Added support for using `set()` with merge options when using
`FirestoreDataConverter`.

# Released
- [changed] All known failure cases for Indexed-related crashes have now been
Expand Down
45 changes: 33 additions & 12 deletions packages/firestore/src/api/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -726,9 +726,15 @@ export class Transaction implements firestore.Transaction {
});
}

set<T>(
documentRef: DocumentReference<T>,
data: Partial<T>,
options: firestore.SetOptions
): Transaction;
set<T>(documentRef: DocumentReference<T>, data: T): Transaction;
set<T>(
documentRef: firestore.DocumentReference<T>,
value: T,
value: T | Partial<T>,
options?: firestore.SetOptions
): Transaction {
validateBetweenNumberOfArgs('Transaction.set', arguments, 2, 3);
Expand All @@ -741,7 +747,8 @@ export class Transaction implements firestore.Transaction {
const [convertedValue, functionName] = applyFirestoreDataConverter(
ref._converter,
value,
'Transaction.set'
'Transaction.set',
options
);
const parsed = this._firestore._dataReader.parseSetData(
functionName,
Expand Down Expand Up @@ -822,9 +829,15 @@ export class WriteBatch implements firestore.WriteBatch {

constructor(private _firestore: Firestore) {}

set<T>(
documentRef: DocumentReference<T>,
data: Partial<T>,
options: firestore.SetOptions
): WriteBatch;
set<T>(documentRef: DocumentReference<T>, data: T): WriteBatch;
set<T>(
documentRef: firestore.DocumentReference<T>,
value: T,
value: T | Partial<T>,
options?: firestore.SetOptions
): WriteBatch {
validateBetweenNumberOfArgs('WriteBatch.set', arguments, 2, 3);
Expand All @@ -838,7 +851,8 @@ export class WriteBatch implements firestore.WriteBatch {
const [convertedValue, functionName] = applyFirestoreDataConverter(
ref._converter,
value,
'WriteBatch.set'
'WriteBatch.set',
options
);
const parsed = this._firestore._dataReader.parseSetData(
functionName,
Expand Down Expand Up @@ -1026,17 +1040,16 @@ export class DocumentReference<T = firestore.DocumentData>
);
}

set(
value: firestore.DocumentData,
options?: firestore.SetOptions
): Promise<void>;
set(value: T, options?: firestore.SetOptions): Promise<void> {
set(value: Partial<T>, options: firestore.SetOptions): Promise<void>;
set(value: T): Promise<void>;
set(value: T | Partial<T>, options?: firestore.SetOptions): Promise<void> {
validateBetweenNumberOfArgs('DocumentReference.set', arguments, 1, 2);
options = validateSetOptions('DocumentReference.set', options);
const [convertedValue, functionName] = applyFirestoreDataConverter(
this._converter,
value,
'DocumentReference.set'
'DocumentReference.set',
options
);
const parsed = this.firestore._dataReader.parseSetData(
functionName,
Expand Down Expand Up @@ -2573,11 +2586,19 @@ function resultChangeType(type: ChangeType): firestore.DocumentChangeType {
export function applyFirestoreDataConverter<T>(
converter: UntypedFirestoreDataConverter<T> | null,
value: T,
functionName: string
functionName: string,
options?: firestore.SetOptions
): [firestore.DocumentData, string] {
let convertedValue;
if (converter) {
convertedValue = converter.toFirestore(value);
if (options && (options.merge || options.mergeFields)) {
// Cast to `any` in order to satisfy the union type constraint on
// toFirestore().
// eslint-disable-next-line @typescript-eslint/no-explicit-any
convertedValue = (converter as any).toFirestore(value, options);
} else {
convertedValue = converter.toFirestore(value);
}
functionName = 'toFirestore() in ' + functionName;
} else {
convertedValue = value as firestore.DocumentData;
Expand Down
Loading