Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,94 @@ void runWriteBatchTests() {
expect(snapshot.exists, false);
});

test('updates with typed data through withConverter', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('with-converter-batch-update');
WriteBatch batch = firestore.batch();

DocumentReference<int> doc = collection.doc('doc1').withConverter(
fromFirestore: (snapshot, options) {
return snapshot.data()!['value'] as int;
},
toFirestore: (value, options) => {'value': value},
);

await doc.set(42);

batch.update<int>(doc, 21);

await batch.commit();

DocumentSnapshot<int> snapshot = await doc.get();
expect(snapshot.exists, isTrue);
expect(snapshot.data(), 21);
});

test('updates complex typed data through withConverter', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('with-converter-complex-batch-update');
DocumentReference<Map<String, dynamic>> rawDoc = collection.doc('doc1');
DocumentReference<_WriteBatchProfile> doc = rawDoc.withConverter(
fromFirestore: (snapshot, options) {
return _WriteBatchProfile.fromFirestore(snapshot.data()!);
},
toFirestore: (value, options) => value.toFirestore(),
);

await rawDoc.set({
'existing': 'preserved',
'name': 'before',
});

WriteBatch batch = firestore.batch();
batch.update<_WriteBatchProfile>(
doc,
_WriteBatchProfile(
name: 'Ada',
score: 42,
address: _WriteBatchAddress(city: 'London', postcode: 'NW1'),
tags: ['admin', 'tester'],
preferences: {
'email': true,
'theme': 'dark',
},
nickname: null,
),
);

await batch.commit();

DocumentSnapshot<Map<String, dynamic>> rawSnapshot = await rawDoc.get();
expect(rawSnapshot.data(), {
'existing': 'preserved',
'name': 'Ada',
'score': 42,
'address': {
'city': 'London',
'postcode': 'NW1',
},
'tags': ['admin', 'tester'],
'preferences': {
'email': true,
'theme': 'dark',
},
'nickname': null,
});

DocumentSnapshot<_WriteBatchProfile> snapshot = await doc.get();
_WriteBatchProfile profile = snapshot.data()!;
expect(profile.name, 'Ada');
expect(profile.score, 42);
expect(profile.address.city, 'London');
expect(profile.address.postcode, 'NW1');
expect(profile.tags, ['admin', 'tester']);
expect(profile.preferences, {
'email': true,
'theme': 'dark',
});
expect(profile.nickname, isNull);
});

test('should update a document using FieldPath keys', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('write-batch-field-path');
Expand Down Expand Up @@ -153,3 +241,69 @@ void runWriteBatchTests() {
});
});
}

class _WriteBatchProfile {
_WriteBatchProfile({
required this.name,
required this.score,
required this.address,
required this.tags,
required this.preferences,
required this.nickname,
});

factory _WriteBatchProfile.fromFirestore(Map<String, dynamic> data) {
return _WriteBatchProfile(
name: data['name'] as String,
score: data['score'] as int,
address: _WriteBatchAddress.fromFirestore(
data['address'] as Map<String, dynamic>,
),
tags: (data['tags'] as List<dynamic>).cast<String>(),
preferences: Map<String, Object?>.from(data['preferences'] as Map),
nickname: data['nickname'] as String?,
);
}

final String name;
final int score;
final _WriteBatchAddress address;
final List<String> tags;
final Map<String, Object?> preferences;
final String? nickname;

Map<String, Object?> toFirestore() {
return {
'name': name,
'score': score,
'address': address.toFirestore(),
'tags': tags,
'preferences': preferences,
'nickname': nickname,
};
}
}

class _WriteBatchAddress {
_WriteBatchAddress({
required this.city,
required this.postcode,
});

factory _WriteBatchAddress.fromFirestore(Map<String, dynamic> data) {
return _WriteBatchAddress(
city: data['city'] as String,
postcode: data['postcode'] as String,
);
}

final String city;
final String postcode;

Map<String, Object?> toFirestore() {
return {
'city': city,
'postcode': postcode,
};
}
}
13 changes: 11 additions & 2 deletions packages/cloud_firestore/cloud_firestore/lib/src/write_batch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,23 @@ class WriteBatch {
/// If the document does not yet exist, an exception will be thrown.
///
/// Objects key can be a String or a FieldPath.
void update(DocumentReference document, Map<Object, Object?> data) {
void update<T>(DocumentReference<T> document, T data) {
assert(
document.firestore == _firestore,
'the document provided is from a different Firestore instance',
);

Map<Object, Object?> firestoreData;
if (data is Map<Object, Object?>) {
firestoreData = data;
} else {
final withConverterDoc = document as _WithConverterDocumentReference<T>;
firestoreData = withConverterDoc._toFirestore(data, null);
}

return _delegate.update(
document.path,
_CodecUtility.replaceValueWithDelegatesInMapFieldPath(data)!,
_CodecUtility.replaceValueWithDelegatesInMapFieldPath(firestoreData)!,
);
}
}
Loading