Skip to content

Commit c845490

Browse files
Merge branch 'mrschmidt/transactions' into mrschmidt/expshim
2 parents 7ecc857 + c46500a commit c845490

File tree

5 files changed

+123
-33
lines changed

5 files changed

+123
-33
lines changed

.changeset/gorgeous-beers-build.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

packages/firestore/exp/index.node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export {
5454
parent
5555
} from '../lite/src/api/reference';
5656

57-
export { runTransaction, Transaction } from '../lite/src/api/transaction';
57+
export { runTransaction, Transaction } from './src/api/transaction';
5858

5959
export {
6060
getDoc,
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as firestore from '../../index';
19+
20+
import { BaseTransaction } from '../../../lite/src/api/transaction';
21+
import { DocumentSnapshot } from './snapshot';
22+
import { TransactionRunner } from '../../../src/core/transaction_runner';
23+
import { AsyncQueue } from '../../../src/util/async_queue';
24+
import { cast } from '../../../lite/src/api/util';
25+
import { Firestore } from './database';
26+
import { Deferred } from '../../../src/util/promise';
27+
import { SnapshotMetadata } from '../../../src/api/database';
28+
import { Transaction as InternalTransaction } from '../../../src/core/transaction';
29+
30+
export class Transaction extends BaseTransaction
31+
implements firestore.Transaction {
32+
constructor(
33+
protected readonly _firestore: Firestore,
34+
_transaction: InternalTransaction
35+
) {
36+
super(_firestore, _transaction);
37+
}
38+
39+
get<T>(
40+
documentRef: firestore.DocumentReference<T>
41+
): Promise<firestore.DocumentSnapshot<T>> {
42+
return this._getHelper<firestore.DocumentSnapshot<T>, T>(
43+
documentRef,
44+
(ref, doc) =>
45+
new DocumentSnapshot<T>(
46+
this._firestore,
47+
ref._key,
48+
doc,
49+
new SnapshotMetadata(
50+
/* hasPendingWrites= */ false,
51+
/* fromCache= */ false
52+
),
53+
ref._converter
54+
)
55+
);
56+
}
57+
}
58+
59+
export function runTransaction<T>(
60+
firestore: firestore.FirebaseFirestore,
61+
updateFunction: (transaction: firestore.Transaction) => Promise<T>
62+
): Promise<T> {
63+
const firestoreClient = cast(firestore, Firestore);
64+
return firestoreClient._getDatastore().then(async datastore => {
65+
const deferred = new Deferred<T>();
66+
new TransactionRunner<T>(
67+
new AsyncQueue(),
68+
datastore,
69+
internalTransaction =>
70+
updateFunction(new Transaction(firestoreClient, internalTransaction)),
71+
deferred
72+
).run();
73+
return deferred.promise;
74+
});
75+
}

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

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,39 @@ import { AsyncQueue } from '../../../src/util/async_queue';
3333
import { Deferred } from '../../../src/util/promise';
3434
import { FieldPath as ExternalFieldPath } from '../../../src/api/field_path';
3535
import { validateReference } from './write_batch';
36-
import { newUserDataReader } from './reference';
36+
import { DocumentReference, newUserDataReader } from './reference';
3737
import { FieldPath } from './field_path';
3838
import { cast } from './util';
3939

40-
export class Transaction implements firestore.Transaction {
41-
// This is the lite version of the Transaction API used in the legacy SDK. The
42-
// class is a close copy but takes different input types.
40+
// TODO(mrschmidt) Consider using `BaseTransaction` as the base class in the
41+
// legacy SDK.
42+
export abstract class BaseTransaction {
43+
// This is the tree-shakeable version of the Transaction class used in the
44+
// legacy SDK. The class is a close copy but takes different input and output
45+
// types. The Lite as well as the firestore-exp SDK further extend this class
46+
// to provide their appropriate API types.
4347

4448
private readonly _dataReader: UserDataReader;
4549

4650
constructor(
47-
private readonly _firestore: Firestore,
51+
protected readonly _firestore: Firestore,
4852
private readonly _transaction: InternalTransaction
4953
) {
5054
this._dataReader = newUserDataReader(_firestore);
5155
}
5256

53-
get<T>(
54-
documentRef: firestore.DocumentReference<T>
55-
): Promise<firestore.DocumentSnapshot<T>> {
57+
/**
58+
* Wrapper around the `BatchGetDocuments` RPC that calls the provided
59+
* converter with the resulting Document. The converter should return
60+
* the QueryDocumentSnapshot type that is expected by the SDK user.
61+
*
62+
* @param documentRef The document to fetch.
63+
* @param converter A function that returns a QueryDocumentSnapshot.
64+
*/
65+
protected _getHelper<DocSnap, T>(
66+
documentRef: firestore.DocumentReference<T>,
67+
converter: (ref: DocumentReference<T>, doc: Document | null) => DocSnap
68+
): Promise<DocSnap> {
5669
const ref = validateReference(documentRef, this._firestore);
5770
return this._transaction
5871
.lookup([ref._key])
@@ -62,19 +75,9 @@ export class Transaction implements firestore.Transaction {
6275
}
6376
const doc = docs[0];
6477
if (doc instanceof NoDocument) {
65-
return new DocumentSnapshot<T>(
66-
this._firestore,
67-
ref._key,
68-
null,
69-
ref._converter
70-
);
78+
return converter(ref, null);
7179
} else if (doc instanceof Document) {
72-
return new DocumentSnapshot<T>(
73-
this._firestore,
74-
doc.key,
75-
doc,
76-
ref._converter
77-
);
80+
return converter(ref, doc);
7881
} else {
7982
throw fail(
8083
`BatchGetDocumentsRequest returned unexpected document type: ${doc.constructor.name}`
@@ -83,17 +86,17 @@ export class Transaction implements firestore.Transaction {
8386
});
8487
}
8588

86-
set<T>(documentRef: firestore.DocumentReference<T>, value: T): Transaction;
89+
set<T>(documentRef: firestore.DocumentReference<T>, value: T): this;
8790
set<T>(
8891
documentRef: firestore.DocumentReference<T>,
8992
value: Partial<T>,
9093
options: firestore.SetOptions
91-
): Transaction;
94+
): this;
9295
set<T>(
9396
documentRef: firestore.DocumentReference<T>,
9497
value: T,
9598
options?: firestore.SetOptions
96-
): Transaction {
99+
): this {
97100
const ref = validateReference(documentRef, this._firestore);
98101
const convertedValue = applyFirestoreDataConverter(
99102
ref._converter,
@@ -114,19 +117,19 @@ export class Transaction implements firestore.Transaction {
114117
update(
115118
documentRef: firestore.DocumentReference<unknown>,
116119
value: firestore.UpdateData
117-
): Transaction;
120+
): this;
118121
update(
119122
documentRef: firestore.DocumentReference<unknown>,
120123
field: string | ExternalFieldPath,
121124
value: unknown,
122125
...moreFieldsAndValues: unknown[]
123-
): Transaction;
126+
): this;
124127
update(
125128
documentRef: firestore.DocumentReference<unknown>,
126129
fieldOrUpdateData: string | ExternalFieldPath | firestore.UpdateData,
127130
value?: unknown,
128131
...moreFieldsAndValues: unknown[]
129-
): Transaction {
132+
): this {
130133
const ref = validateReference(documentRef, this._firestore);
131134

132135
let parsed;
@@ -153,13 +156,26 @@ export class Transaction implements firestore.Transaction {
153156
return this;
154157
}
155158

156-
delete(documentRef: firestore.DocumentReference<unknown>): Transaction {
159+
delete(documentRef: firestore.DocumentReference<unknown>): this {
157160
const ref = validateReference(documentRef, this._firestore);
158161
this._transaction.delete(ref._key);
159162
return this;
160163
}
161164
}
162165

166+
export class Transaction extends BaseTransaction
167+
implements firestore.Transaction {
168+
get<T>(
169+
documentRef: firestore.DocumentReference<T>
170+
): Promise<firestore.DocumentSnapshot<T>> {
171+
return this._getHelper<DocumentSnapshot<T>, T>(
172+
documentRef,
173+
(ref, doc) =>
174+
new DocumentSnapshot<T>(this._firestore, ref._key, doc, ref._converter)
175+
);
176+
}
177+
}
178+
163179
export function runTransaction<T>(
164180
firestore: firestore.FirebaseFirestore,
165181
updateFunction: (transaction: firestore.Transaction) => Promise<T>

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ import {
2323
} from '../../../src/model/mutation';
2424
import { Code, FirestoreError } from '../../../src/util/error';
2525
import { applyFirestoreDataConverter } from '../../../src/api/database';
26-
import {
27-
DocumentKeyReference,
28-
UserDataReader
29-
} from '../../../src/api/user_data_reader';
26+
import { UserDataReader } from '../../../src/api/user_data_reader';
3027
import { cast } from './util';
3128
import { DocumentReference, newUserDataReader } from './reference';
3229
import { Firestore } from './database';
@@ -159,7 +156,7 @@ export class WriteBatch implements firestore.WriteBatch {
159156
export function validateReference<T>(
160157
documentRef: firestore.DocumentReference<T>,
161158
firestore: Firestore
162-
): DocumentKeyReference<T> {
159+
): DocumentReference<T> {
163160
if (documentRef.firestore !== firestore) {
164161
throw new FirestoreError(
165162
Code.INVALID_ARGUMENT,

0 commit comments

Comments
 (0)