Skip to content

Commit 39ca8ec

Browse files
author
Brian Chen
authored
add support for set() with SetOptions when using FirestoreDataConverter (#3254)
1 parent 8e0c036 commit 39ca8ec

File tree

9 files changed

+294
-59
lines changed

9 files changed

+294
-59
lines changed

.changeset/quiet-coats-type.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'firebase': minor
3+
'@firebase/firestore': minor
4+
'@firebase/firestore-types': minor
5+
---
6+
7+
Added support for `set()` with merge options when using `FirestoreDataConverter`.

packages/firebase/index.d.ts

+41-6
Original file line numberDiff line numberDiff line change
@@ -7859,9 +7859,11 @@ declare namespace firebase.firestore {
78597859
/**
78607860
* Called by the Firestore SDK to convert a custom model object of type T
78617861
* into a plain Javascript object (suitable for writing directly to the
7862-
* Firestore database).
7862+
* Firestore database). To use `set()` with `merge` and `mergeFields`,
7863+
* `toFirestore()` must be defined with `Partial<T>`.
78637864
*/
78647865
toFirestore(modelObject: T): DocumentData;
7866+
toFirestore(modelObject: Partial<T>, options: SetOptions): DocumentData;
78657867

78667868
/**
78677869
* Called by the Firestore SDK to convert Firestore data into an object of
@@ -8310,10 +8312,21 @@ declare namespace firebase.firestore {
83108312
*/
83118313
set<T>(
83128314
documentRef: DocumentReference<T>,
8313-
data: T,
8314-
options?: SetOptions
8315+
data: Partial<T>,
8316+
options: SetOptions
83158317
): Transaction;
83168318

8319+
/**
8320+
* Writes to the document referred to by the provided `DocumentReference`.
8321+
* If the document does not exist yet, it will be created. If you pass
8322+
* `SetOptions`, the provided data can be merged into the existing document.
8323+
*
8324+
* @param documentRef A reference to the document to be set.
8325+
* @param data An object of the fields and values for the document.
8326+
* @return This `Transaction` instance. Used for chaining method calls.
8327+
*/
8328+
set<T>(documentRef: DocumentReference<T>, data: T): Transaction;
8329+
83178330
/**
83188331
* Updates fields in the document referred to by the provided
83198332
* `DocumentReference`. The update will fail if applied to a document that
@@ -8384,10 +8397,21 @@ declare namespace firebase.firestore {
83848397
*/
83858398
set<T>(
83868399
documentRef: DocumentReference<T>,
8387-
data: T,
8388-
options?: SetOptions
8400+
data: Partial<T>,
8401+
options: SetOptions
83898402
): WriteBatch;
83908403

8404+
/**
8405+
* Writes to the document referred to by the provided `DocumentReference`.
8406+
* If the document does not exist yet, it will be created. If you pass
8407+
* `SetOptions`, the provided data can be merged into the existing document.
8408+
*
8409+
* @param documentRef A reference to the document to be set.
8410+
* @param data An object of the fields and values for the document.
8411+
* @return This `WriteBatch` instance. Used for chaining method calls.
8412+
*/
8413+
set<T>(documentRef: DocumentReference<T>, data: T): WriteBatch;
8414+
83918415
/**
83928416
* Updates fields in the document referred to by the provided
83938417
* `DocumentReference`. The update will fail if applied to a document that
@@ -8566,7 +8590,18 @@ declare namespace firebase.firestore {
85668590
* @return A Promise resolved once the data has been successfully written
85678591
* to the backend (Note that it won't resolve while you're offline).
85688592
*/
8569-
set(data: T, options?: SetOptions): Promise<void>;
8593+
set(data: Partial<T>, options: SetOptions): Promise<void>;
8594+
8595+
/**
8596+
* Writes to the document referred to by this `DocumentReference`. If the
8597+
* document does not yet exist, it will be created. If you pass
8598+
* `SetOptions`, the provided data can be merged into an existing document.
8599+
*
8600+
* @param data A map of the fields and values for the document.
8601+
* @return A Promise resolved once the data has been successfully written
8602+
* to the backend (Note that it won't resolve while you're offline).
8603+
*/
8604+
set(data: T): Promise<void>;
85708605

85718606
/**
85728607
* Updates fields in the document referred to by this `DocumentReference`.

packages/firestore-types/index.d.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export function setLogLevel(logLevel: LogLevel): void;
5050

5151
export interface FirestoreDataConverter<T> {
5252
toFirestore(modelObject: T): DocumentData;
53+
toFirestore(modelObject: Partial<T>, options: SetOptions): DocumentData;
5354

5455
fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): T;
5556
}
@@ -146,9 +147,10 @@ export class Transaction {
146147

147148
set<T>(
148149
documentRef: DocumentReference<T>,
149-
data: T,
150-
options?: SetOptions
150+
data: Partial<T>,
151+
options: SetOptions
151152
): Transaction;
153+
set<T>(documentRef: DocumentReference<T>, data: T): Transaction;
152154

153155
update(documentRef: DocumentReference<any>, data: UpdateData): Transaction;
154156
update(
@@ -166,9 +168,10 @@ export class WriteBatch {
166168

167169
set<T>(
168170
documentRef: DocumentReference<T>,
169-
data: T,
170-
options?: SetOptions
171+
data: Partial<T>,
172+
options: SetOptions
171173
): WriteBatch;
174+
set<T>(documentRef: DocumentReference<T>, data: T): WriteBatch;
172175

173176
update(documentRef: DocumentReference<any>, data: UpdateData): WriteBatch;
174177
update(
@@ -208,7 +211,8 @@ export class DocumentReference<T = DocumentData> {
208211

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

211-
set(data: T, options?: SetOptions): Promise<void>;
214+
set(data: Partial<T>, options: SetOptions): Promise<void>;
215+
set(data: T): Promise<void>;
212216

213217
update(data: UpdateData): Promise<void>;
214218
update(

packages/firestore/lite/src/api/reference.ts

+10-11
Original file line numberDiff line numberDiff line change
@@ -456,16 +456,13 @@ export function setDoc<T>(
456456
): Promise<void> {
457457
const ref = cast(reference, DocumentReference);
458458

459-
const [convertedValue] = applyFirestoreDataConverter(
460-
ref._converter,
461-
data,
462-
'setDoc'
463-
);
459+
const convertedValue = applyFirestoreDataConverter(ref._converter, data);
464460
const dataReader = newUserDataReader(ref.firestore);
465461
const parsed = dataReader.parseSetData(
466462
'setDoc',
467463
ref._key,
468464
convertedValue,
465+
ref._converter !== null,
469466
options
470467
);
471468

@@ -548,14 +545,16 @@ export function addDoc<T>(
548545
const collRef = cast(reference, CollectionReference);
549546
const docRef = doc(collRef);
550547

551-
const [convertedValue] = applyFirestoreDataConverter(
552-
collRef._converter,
553-
data,
554-
'addDoc'
555-
);
548+
const convertedValue = applyFirestoreDataConverter(collRef._converter, data);
556549

557550
const dataReader = newUserDataReader(collRef.firestore);
558-
const parsed = dataReader.parseSetData('addDoc', docRef._key, convertedValue);
551+
const parsed = dataReader.parseSetData(
552+
'addDoc',
553+
docRef._key,
554+
convertedValue,
555+
docRef._converter !== null,
556+
{}
557+
);
559558

560559
return collRef.firestore
561560
._getDatastore()

packages/firestore/lite/src/api/transaction.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,12 @@ export class Transaction implements firestore.Transaction {
9595
options?: firestore.SetOptions
9696
): Transaction {
9797
const ref = validateReference(documentRef, this._firestore);
98-
const [convertedValue] = applyFirestoreDataConverter(
99-
ref._converter,
100-
value,
101-
'Transaction.set'
102-
);
98+
const convertedValue = applyFirestoreDataConverter(ref._converter, value);
10399
const parsed = this._dataReader.parseSetData(
104100
'Transaction.set',
105101
ref._key,
106102
convertedValue,
103+
ref._converter !== null,
107104
options
108105
);
109106
this._transaction.set(ref._key, parsed);

packages/firestore/lite/src/api/write_batch.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,13 @@ export class WriteBatch implements firestore.WriteBatch {
5959
this.verifyNotCommitted();
6060
const ref = validateReference(documentRef, this._firestore);
6161

62-
const [convertedValue] = applyFirestoreDataConverter(
63-
ref._converter,
64-
value,
65-
'WriteBatch.set'
66-
);
62+
const convertedValue = applyFirestoreDataConverter(ref._converter, value);
6763

6864
const parsed = this._dataReader.parseSetData(
6965
'WriteBatch.set',
7066
ref._key,
7167
convertedValue,
68+
ref._converter !== null,
7269
options
7370
);
7471
this._mutations = this._mutations.concat(

packages/firestore/src/api/database.ts

+40-21
Original file line numberDiff line numberDiff line change
@@ -726,9 +726,15 @@ export class Transaction implements firestore.Transaction {
726726
});
727727
}
728728

729+
set<T>(
730+
documentRef: DocumentReference<T>,
731+
data: Partial<T>,
732+
options: firestore.SetOptions
733+
): Transaction;
734+
set<T>(documentRef: DocumentReference<T>, data: T): Transaction;
729735
set<T>(
730736
documentRef: firestore.DocumentReference<T>,
731-
value: T,
737+
value: T | Partial<T>,
732738
options?: firestore.SetOptions
733739
): Transaction {
734740
validateBetweenNumberOfArgs('Transaction.set', arguments, 2, 3);
@@ -738,15 +744,16 @@ export class Transaction implements firestore.Transaction {
738744
this._firestore
739745
);
740746
options = validateSetOptions('Transaction.set', options);
741-
const [convertedValue, functionName] = applyFirestoreDataConverter(
747+
const convertedValue = applyFirestoreDataConverter(
742748
ref._converter,
743749
value,
744-
'Transaction.set'
750+
options
745751
);
746752
const parsed = this._firestore._dataReader.parseSetData(
747-
functionName,
753+
'Transaction.set',
748754
ref._key,
749755
convertedValue,
756+
ref._converter !== null,
750757
options
751758
);
752759
this._transaction.set(ref._key, parsed);
@@ -825,9 +832,15 @@ export class WriteBatch implements firestore.WriteBatch {
825832

826833
constructor(private _firestore: Firestore) {}
827834

835+
set<T>(
836+
documentRef: DocumentReference<T>,
837+
data: Partial<T>,
838+
options: firestore.SetOptions
839+
): WriteBatch;
840+
set<T>(documentRef: DocumentReference<T>, data: T): WriteBatch;
828841
set<T>(
829842
documentRef: firestore.DocumentReference<T>,
830-
value: T,
843+
value: T | Partial<T>,
831844
options?: firestore.SetOptions
832845
): WriteBatch {
833846
validateBetweenNumberOfArgs('WriteBatch.set', arguments, 2, 3);
@@ -838,15 +851,16 @@ export class WriteBatch implements firestore.WriteBatch {
838851
this._firestore
839852
);
840853
options = validateSetOptions('WriteBatch.set', options);
841-
const [convertedValue, functionName] = applyFirestoreDataConverter(
854+
const convertedValue = applyFirestoreDataConverter(
842855
ref._converter,
843856
value,
844-
'WriteBatch.set'
857+
options
845858
);
846859
const parsed = this._firestore._dataReader.parseSetData(
847-
functionName,
860+
'WriteBatch.set',
848861
ref._key,
849862
convertedValue,
863+
ref._converter !== null,
850864
options
851865
);
852866
this._mutations = this._mutations.concat(
@@ -1032,22 +1046,21 @@ export class DocumentReference<T = firestore.DocumentData>
10321046
);
10331047
}
10341048

1035-
set(
1036-
value: firestore.DocumentData,
1037-
options?: firestore.SetOptions
1038-
): Promise<void>;
1039-
set(value: T, options?: firestore.SetOptions): Promise<void> {
1049+
set(value: Partial<T>, options: firestore.SetOptions): Promise<void>;
1050+
set(value: T): Promise<void>;
1051+
set(value: T | Partial<T>, options?: firestore.SetOptions): Promise<void> {
10401052
validateBetweenNumberOfArgs('DocumentReference.set', arguments, 1, 2);
10411053
options = validateSetOptions('DocumentReference.set', options);
1042-
const [convertedValue, functionName] = applyFirestoreDataConverter(
1054+
const convertedValue = applyFirestoreDataConverter(
10431055
this._converter,
10441056
value,
1045-
'DocumentReference.set'
1057+
options
10461058
);
10471059
const parsed = this.firestore._dataReader.parseSetData(
1048-
functionName,
1060+
'DocumentReference.set',
10491061
this._key,
10501062
convertedValue,
1063+
this._converter !== null,
10511064
options
10521065
);
10531066
return this._firestoreClient.write(
@@ -2584,16 +2597,22 @@ function resultChangeType(type: ChangeType): firestore.DocumentChangeType {
25842597
export function applyFirestoreDataConverter<T>(
25852598
converter: UntypedFirestoreDataConverter<T> | null,
25862599
value: T,
2587-
functionName: string
2588-
): [firestore.DocumentData, string] {
2600+
options?: firestore.SetOptions
2601+
): firestore.DocumentData {
25892602
let convertedValue;
25902603
if (converter) {
2591-
convertedValue = converter.toFirestore(value);
2592-
functionName = 'toFirestore() in ' + functionName;
2604+
if (options && (options.merge || options.mergeFields)) {
2605+
// Cast to `any` in order to satisfy the union type constraint on
2606+
// toFirestore().
2607+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2608+
convertedValue = (converter as any).toFirestore(value, options);
2609+
} else {
2610+
convertedValue = converter.toFirestore(value);
2611+
}
25932612
} else {
25942613
convertedValue = value as firestore.DocumentData;
25952614
}
2596-
return [convertedValue, functionName];
2615+
return convertedValue;
25972616
}
25982617

25992618
function contains(obj: object, key: string): obj is { key: unknown } {

0 commit comments

Comments
 (0)