@causa/runtime-google 0.39.1 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -14
- package/dist/app-check/testing.d.ts +7 -3
- package/dist/app-check/testing.js +9 -6
- package/dist/firebase/module.d.ts +14 -2
- package/dist/firebase/module.js +45 -16
- package/dist/firebase/testing.d.ts +7 -4
- package/dist/firebase/testing.js +12 -7
- package/dist/firestore/testing.d.ts +32 -17
- package/dist/firestore/testing.js +45 -29
- package/dist/identity-platform/testing.d.ts +21 -6
- package/dist/identity-platform/testing.js +34 -9
- package/dist/pubsub/publisher.module.d.ts +1 -1
- package/dist/pubsub/{testing/fixture.d.ts → testing.d.ts} +64 -48
- package/dist/pubsub/{testing/fixture.js → testing.js} +106 -73
- package/dist/spanner/column.decorator.d.ts +1 -11
- package/dist/spanner/column.decorator.js +2 -7
- package/dist/spanner/conversion.d.ts +5 -18
- package/dist/spanner/conversion.js +45 -125
- package/dist/spanner/entity-manager.d.ts +19 -23
- package/dist/spanner/entity-manager.js +26 -60
- package/dist/spanner/table-cache.js +0 -3
- package/dist/spanner/testing.d.ts +37 -18
- package/dist/spanner/testing.js +75 -10
- package/dist/spanner/types.d.ts +0 -6
- package/dist/testing.d.ts +36 -2
- package/dist/testing.js +33 -2
- package/dist/transaction/firestore-pubsub/index.d.ts +3 -2
- package/dist/transaction/firestore-pubsub/index.js +2 -1
- package/dist/transaction/firestore-pubsub/nestjs-collection-resolver.d.ts +1 -1
- package/dist/transaction/firestore-pubsub/nestjs-collection-resolver.js +0 -1
- package/dist/transaction/firestore-pubsub/readonly-state-transaction.d.ts +37 -0
- package/dist/transaction/firestore-pubsub/readonly-state-transaction.js +46 -0
- package/dist/transaction/firestore-pubsub/runner.d.ts +6 -4
- package/dist/transaction/firestore-pubsub/runner.js +16 -7
- package/dist/transaction/firestore-pubsub/state-transaction.d.ts +11 -49
- package/dist/transaction/firestore-pubsub/state-transaction.js +19 -42
- package/dist/transaction/firestore-pubsub/transaction.d.ts +15 -3
- package/dist/transaction/firestore-pubsub/transaction.js +21 -3
- package/dist/transaction/firestore-pubsub/types.d.ts +36 -0
- package/dist/transaction/firestore-pubsub/types.js +1 -0
- package/dist/transaction/index.d.ts +0 -3
- package/dist/transaction/index.js +0 -3
- package/dist/transaction/spanner-outbox/index.d.ts +3 -1
- package/dist/transaction/spanner-outbox/index.js +3 -0
- package/dist/transaction/spanner-outbox/readonly-transaction.d.ts +22 -0
- package/dist/transaction/spanner-outbox/readonly-transaction.js +25 -0
- package/dist/transaction/spanner-outbox/runner.d.ts +7 -18
- package/dist/transaction/spanner-outbox/runner.js +13 -6
- package/dist/transaction/{spanner-utils.js → spanner-outbox/spanner-utils.js} +1 -1
- package/dist/transaction/spanner-outbox/state-transaction.d.ts +20 -0
- package/dist/transaction/spanner-outbox/state-transaction.js +34 -0
- package/dist/transaction/spanner-outbox/transaction.d.ts +28 -0
- package/dist/transaction/spanner-outbox/transaction.js +38 -0
- package/package.json +37 -34
- package/dist/pubsub/testing/index.d.ts +0 -4
- package/dist/pubsub/testing/index.js +0 -2
- package/dist/pubsub/testing/requester.d.ts +0 -50
- package/dist/pubsub/testing/requester.js +0 -53
- package/dist/testing/google-app-fixture.d.ts +0 -191
- package/dist/testing/google-app-fixture.js +0 -200
- package/dist/testing/index.d.ts +0 -1
- package/dist/testing/index.js +0 -1
- package/dist/transaction/spanner-pubsub/index.d.ts +0 -2
- package/dist/transaction/spanner-pubsub/index.js +0 -2
- package/dist/transaction/spanner-pubsub/module.d.ts +0 -14
- package/dist/transaction/spanner-pubsub/module.js +0 -21
- package/dist/transaction/spanner-pubsub/runner.d.ts +0 -23
- package/dist/transaction/spanner-pubsub/runner.js +0 -69
- package/dist/transaction/spanner-state-transaction.d.ts +0 -20
- package/dist/transaction/spanner-state-transaction.js +0 -35
- package/dist/transaction/spanner-transaction.d.ts +0 -16
- package/dist/transaction/spanner-transaction.js +0 -20
- /package/dist/transaction/{spanner-utils.d.ts → spanner-outbox/spanner-utils.d.ts} +0 -0
|
@@ -7,38 +7,23 @@ import { getSpannerColumnsMetadata, } from './column.decorator.js';
|
|
|
7
7
|
*
|
|
8
8
|
* @param spannerObject The object returned by Spanner that should be converted back to a class instance.
|
|
9
9
|
* @param type The class for the object.
|
|
10
|
-
* @param options Used for recursion and should not be set directly.
|
|
11
10
|
* @returns The created object.
|
|
12
11
|
*/
|
|
13
|
-
function
|
|
12
|
+
export function spannerObjectToInstance(spannerObject, type) {
|
|
14
13
|
const columnsMetadata = getSpannerColumnsMetadata(type);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
else if (Array.isArray(spannerObject[columnName])) {
|
|
30
|
-
plain[property] = spannerObject[columnName].map((v) => spannerValueToJavaScript(v, columnMetadata));
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
plain[property] = spannerValueToJavaScript(spannerObject[columnName], columnMetadata);
|
|
34
|
-
}
|
|
35
|
-
if (plain[property] != null) {
|
|
36
|
-
hasAtLeastOneNonNullValue = true;
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
return hasAtLeastOneNonNullValue || !options.nullifyInstance
|
|
40
|
-
? plainToInstance(type, plain)
|
|
41
|
-
: null;
|
|
14
|
+
const plain = Object.fromEntries(Object.entries(columnsMetadata).map(([property, columnMetadata]) => {
|
|
15
|
+
const columnName = columnMetadata.name;
|
|
16
|
+
const value = spannerObject[columnName];
|
|
17
|
+
if (columnMetadata.isJson) {
|
|
18
|
+
return [property, value];
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
const values = value.map((v) => spannerValueToJavaScript(v, columnMetadata));
|
|
22
|
+
return [property, values];
|
|
23
|
+
}
|
|
24
|
+
return [property, spannerValueToJavaScript(value, columnMetadata)];
|
|
25
|
+
}));
|
|
26
|
+
return plainToInstance(type, plain);
|
|
42
27
|
}
|
|
43
28
|
/**
|
|
44
29
|
* Converts a scalar value returned by Spanner to a regular JavaScript value.
|
|
@@ -54,17 +39,15 @@ function spannerValueToJavaScript(value, columnMetadata) {
|
|
|
54
39
|
// Floats are always safe and can always be unwrapped.
|
|
55
40
|
return value.valueOf();
|
|
56
41
|
}
|
|
57
|
-
|
|
42
|
+
if (value instanceof Int) {
|
|
58
43
|
if (columnMetadata.isBigInt) {
|
|
59
44
|
return BigInt(value.value);
|
|
60
45
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return value.valueOf();
|
|
65
|
-
}
|
|
46
|
+
// This unwraps the string to an integer number, but might throw if the integer is above what can be represented
|
|
47
|
+
// safely as a float.
|
|
48
|
+
return value.valueOf();
|
|
66
49
|
}
|
|
67
|
-
|
|
50
|
+
if (value instanceof Date &&
|
|
68
51
|
// `PreciseDate` extends `Date`. This was previously used to handle conflicting versions of `PreciseDate`.
|
|
69
52
|
value.constructor.name === PreciseDate.name &&
|
|
70
53
|
!columnMetadata.isPreciseDate) {
|
|
@@ -74,17 +57,6 @@ function spannerValueToJavaScript(value, columnMetadata) {
|
|
|
74
57
|
}
|
|
75
58
|
return value;
|
|
76
59
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Creates a typed class instance from an object returned by the Spanner API.
|
|
79
|
-
*
|
|
80
|
-
* @param spannerObject The object returned by Spanner that should be converted back to a class instance.
|
|
81
|
-
* @param type The class for the object.
|
|
82
|
-
* @returns The created object.
|
|
83
|
-
*/
|
|
84
|
-
export function spannerObjectToInstance(spannerObject, type) {
|
|
85
|
-
// This is okay as `null` can only be returned when the internal option `nullifyInstance` is set.
|
|
86
|
-
return spannerObjectToInstanceWithOptions(spannerObject, type);
|
|
87
|
-
}
|
|
88
60
|
/**
|
|
89
61
|
* Converts a value such that it is safe to pass to the Spanner client.
|
|
90
62
|
* Numeric values and arrays of numeric values are wrapped using Spanner classes.
|
|
@@ -103,50 +75,19 @@ function makeSpannerValue(value, metadata) {
|
|
|
103
75
|
? value.map((v) => new Int(v.toString()))
|
|
104
76
|
: new Int(value.toString());
|
|
105
77
|
}
|
|
106
|
-
|
|
78
|
+
if (metadata.isJson) {
|
|
107
79
|
return JSON.stringify(value);
|
|
108
80
|
}
|
|
109
|
-
|
|
81
|
+
if (typeof value === 'number') {
|
|
110
82
|
return new Float(value);
|
|
111
83
|
}
|
|
112
|
-
|
|
84
|
+
if (Array.isArray(value) &&
|
|
113
85
|
value.length > 0 &&
|
|
114
86
|
typeof value[0] === 'number') {
|
|
115
87
|
return value.map((v) => new Float(v));
|
|
116
88
|
}
|
|
117
89
|
return value;
|
|
118
90
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Converts a class object to a plain JavaScript object that can be passed to the Spanner API.
|
|
121
|
-
*
|
|
122
|
-
* @param instance The object to convert to a Spanner object.
|
|
123
|
-
* @param type The type of the object to convert.
|
|
124
|
-
* @param columnNamePrefix The prefix to add to column names. Used for recursion and should not be set directly.
|
|
125
|
-
* @returns A generic JavaScript object that can be passed to the Spanner API.
|
|
126
|
-
*/
|
|
127
|
-
export function instanceToSpannerObjectInternal(instance, type, columnNamePrefix = '') {
|
|
128
|
-
const columnsMetadata = getSpannerColumnsMetadata(type);
|
|
129
|
-
let spannerObject = {};
|
|
130
|
-
Object.entries(columnsMetadata).forEach(([property, columnMetadata]) => {
|
|
131
|
-
const columnName = `${columnNamePrefix}${columnMetadata.name}`;
|
|
132
|
-
// When the current property value is `undefined`, column(s) values should not be set.
|
|
133
|
-
if (instance !== null && instance[property] === undefined) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
// When the whole instance is `null`, all its child properties should be set to null.
|
|
137
|
-
const propertyValue = instance === null ? null : instance[property];
|
|
138
|
-
if (columnMetadata.nestedType) {
|
|
139
|
-
spannerObject = {
|
|
140
|
-
...spannerObject,
|
|
141
|
-
...instanceToSpannerObjectInternal(propertyValue, columnMetadata.nestedType, `${columnName}_`),
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
spannerObject[columnName] = makeSpannerValue(propertyValue, columnMetadata);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
return spannerObject;
|
|
149
|
-
}
|
|
150
91
|
/**
|
|
151
92
|
* Converts a class object to a plain JavaScript object that can be passed to the Spanner API.
|
|
152
93
|
*
|
|
@@ -155,11 +96,18 @@ export function instanceToSpannerObjectInternal(instance, type, columnNamePrefix
|
|
|
155
96
|
* @returns A generic JavaScript object that can be passed to the Spanner API.
|
|
156
97
|
*/
|
|
157
98
|
export function instanceToSpannerObject(instance, type) {
|
|
158
|
-
|
|
99
|
+
const columnsMetadata = getSpannerColumnsMetadata(type);
|
|
100
|
+
return Object.fromEntries(Object.entries(columnsMetadata)
|
|
101
|
+
.map(([p, m]) => [m, instance[p]])
|
|
102
|
+
// When the current property value is `undefined`, the column value should not be set.
|
|
103
|
+
.filter(([, v]) => v !== undefined)
|
|
104
|
+
.map(([metadata, value]) => [
|
|
105
|
+
metadata.name,
|
|
106
|
+
makeSpannerValue(value, metadata),
|
|
107
|
+
]));
|
|
159
108
|
}
|
|
160
109
|
/**
|
|
161
|
-
* Copies an instance,
|
|
162
|
-
* Columns with the {@link SpannerColumnMetadata.nullifyNested} option set to `true` are also set to `null`.
|
|
110
|
+
* Copies an instance, setting all columns that are not defined in the instance to `null`.
|
|
163
111
|
*
|
|
164
112
|
* @param instance The instance to copy.
|
|
165
113
|
* @param type The type of the instance.
|
|
@@ -167,30 +115,14 @@ export function instanceToSpannerObject(instance, type) {
|
|
|
167
115
|
*/
|
|
168
116
|
export function copyInstanceWithMissingColumnsToNull(instance, type) {
|
|
169
117
|
const columnsMetadata = getSpannerColumnsMetadata(type);
|
|
170
|
-
const plain = {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (columnMetadata.nullifyNested && instanceValue == null) {
|
|
175
|
-
plain[property] = null;
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
plain[property] = copyInstanceWithMissingColumnsToNull(instanceValue, columnMetadata.nestedType);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
else if (instanceValue !== undefined) {
|
|
182
|
-
plain[property] = instanceValue;
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
plain[property] = null;
|
|
186
|
-
}
|
|
187
|
-
});
|
|
118
|
+
const plain = Object.fromEntries(Object.keys(columnsMetadata).map((property) => {
|
|
119
|
+
const value = instance[property];
|
|
120
|
+
return [property, value === undefined ? null : value];
|
|
121
|
+
}));
|
|
188
122
|
return plainToInstance(type, plain);
|
|
189
123
|
}
|
|
190
124
|
/**
|
|
191
|
-
*
|
|
192
|
-
* Updates are applied "column-wise", which means that recursion stops at properties decorated as columns.
|
|
193
|
-
* For example, JSON values are not affected.
|
|
125
|
+
* Updates an instance with the values from the update.
|
|
194
126
|
*
|
|
195
127
|
* @param instance The instance to update. It should be a full, typed, instance, unless `type` is passed as well.
|
|
196
128
|
* @param update The update to apply to the instance.
|
|
@@ -200,25 +132,13 @@ export function copyInstanceWithMissingColumnsToNull(instance, type) {
|
|
|
200
132
|
export function updateInstanceByColumn(instance, update, type) {
|
|
201
133
|
type ??= instance.constructor;
|
|
202
134
|
const columnsMetadata = getSpannerColumnsMetadata(type);
|
|
203
|
-
const plain = {
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (columnMetadata.nestedType) {
|
|
212
|
-
if (columnMetadata.nullifyNested && updateValue === null) {
|
|
213
|
-
plain[property] = null;
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
plain[property] = updateInstanceByColumn(instanceValue, updateValue, columnMetadata.nestedType);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
else if (updateValue !== undefined) {
|
|
220
|
-
plain[property] = updateValue;
|
|
221
|
-
}
|
|
222
|
-
});
|
|
135
|
+
const plain = Object.fromEntries(Object.keys(columnsMetadata).map((property) => {
|
|
136
|
+
const instanceValue = instance[property];
|
|
137
|
+
const updateValue = update[property];
|
|
138
|
+
return [
|
|
139
|
+
property,
|
|
140
|
+
updateValue === undefined ? instanceValue : updateValue,
|
|
141
|
+
];
|
|
142
|
+
}));
|
|
223
143
|
return plainToInstance(type, plain);
|
|
224
144
|
}
|
|
@@ -3,7 +3,7 @@ import { type Type as ParamType } from '@google-cloud/spanner/build/src/codec.js
|
|
|
3
3
|
import type { TimestampBounds } from '@google-cloud/spanner/build/src/transaction.js';
|
|
4
4
|
import { type Type } from '@nestjs/common';
|
|
5
5
|
import { SpannerTableCache } from './table-cache.js';
|
|
6
|
-
import type {
|
|
6
|
+
import type { SpannerReadOnlyTransactionOption, SpannerReadWriteTransactionOption } from './types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Any Spanner transaction that can be used for reading.
|
|
9
9
|
*/
|
|
@@ -19,7 +19,7 @@ export type SpannerKey = (string | null)[];
|
|
|
19
19
|
/**
|
|
20
20
|
* Options for {@link SpannerEntityManager.snapshot}.
|
|
21
21
|
*/
|
|
22
|
-
type SnapshotOptions = {
|
|
22
|
+
type SnapshotOptions = SpannerReadOnlyTransactionOption | {
|
|
23
23
|
/**
|
|
24
24
|
* Sets how the timestamp will be selected when creating the snapshot.
|
|
25
25
|
*/
|
|
@@ -29,6 +29,10 @@ type SnapshotOptions = {
|
|
|
29
29
|
* A function that can be passed to the {@link SpannerEntityManager.snapshot} method.
|
|
30
30
|
*/
|
|
31
31
|
export type SnapshotFunction<T> = (snapshot: SpannerReadOnlyTransaction) => Promise<T>;
|
|
32
|
+
/**
|
|
33
|
+
* A function that can be passed to the {@link SpannerEntityManager.transaction} method.
|
|
34
|
+
*/
|
|
35
|
+
export type SpannerTransactionFunction<T> = (transaction: SpannerReadWriteTransaction) => Promise<T>;
|
|
32
36
|
/**
|
|
33
37
|
* A SQL statement run using {@link SpannerEntityManager.query}.
|
|
34
38
|
*/
|
|
@@ -98,7 +102,7 @@ export declare class SpannerEntityManager {
|
|
|
98
102
|
* constructor).
|
|
99
103
|
* @returns The primary key of the entity.
|
|
100
104
|
*/
|
|
101
|
-
getPrimaryKey<T>(entity: T |
|
|
105
|
+
getPrimaryKey<T>(entity: T | Partial<T>, entityType?: Type<T>): SpannerKey;
|
|
102
106
|
/**
|
|
103
107
|
* Returns the primary key of the given Spanner object, assumed to be an entity of the given type.
|
|
104
108
|
*
|
|
@@ -189,7 +193,17 @@ export declare class SpannerEntityManager {
|
|
|
189
193
|
* @param runFn The function to run in the transaction.
|
|
190
194
|
* @returns The return value of the function.
|
|
191
195
|
*/
|
|
192
|
-
transaction<T>(runFn:
|
|
196
|
+
transaction<T>(runFn: SpannerTransactionFunction<T>): Promise<T>;
|
|
197
|
+
/**
|
|
198
|
+
* Runs the provided function in a (read write) {@link SpannerReadWriteTransaction}.
|
|
199
|
+
* The function itself should not commit or rollback the transaction.
|
|
200
|
+
* If the function throws an error, the transaction will be rolled back.
|
|
201
|
+
*
|
|
202
|
+
* @param options The options to use when creating the transaction.
|
|
203
|
+
* @param runFn The function to run in the transaction.
|
|
204
|
+
* @returns The return value of the function.
|
|
205
|
+
*/
|
|
206
|
+
transaction<T>(options: SpannerReadWriteTransactionOption, runFn: SpannerTransactionFunction<T>): Promise<T>;
|
|
193
207
|
/**
|
|
194
208
|
* Runs the provided function in a {@link SpannerReadOnlyTransaction}.
|
|
195
209
|
* The snapshot will be automatically released when the function returns.
|
|
@@ -288,7 +302,7 @@ export declare class SpannerEntityManager {
|
|
|
288
302
|
* @param options Options for the operation.
|
|
289
303
|
* @returns The updated entity.
|
|
290
304
|
*/
|
|
291
|
-
update<T>(entityType: Type<T>, update:
|
|
305
|
+
update<T>(entityType: Type<T>, update: Partial<T>, options?: SpannerReadWriteTransactionOption & Pick<FindOptions, 'includeSoftDeletes'> & {
|
|
292
306
|
/**
|
|
293
307
|
* A function that will be called with the entity before it is updated.
|
|
294
308
|
* This function can throw an error to prevent the update.
|
|
@@ -316,23 +330,5 @@ export declare class SpannerEntityManager {
|
|
|
316
330
|
*/
|
|
317
331
|
validateFn?: (entity: T) => void;
|
|
318
332
|
}): Promise<T>;
|
|
319
|
-
/**
|
|
320
|
-
* Runs the given "read-write" function on a transaction. If a transaction is not passed, a new
|
|
321
|
-
* {@link SpannerReadWriteTransaction} is created instead.
|
|
322
|
-
*
|
|
323
|
-
* @param transaction The transaction to use. If `undefined`, a new transaction is created.
|
|
324
|
-
* @param fn The function to run on the transaction.
|
|
325
|
-
* @returns The result of the function.
|
|
326
|
-
*/
|
|
327
|
-
runInExistingOrNewTransaction<T>(transaction: SpannerReadWriteTransaction | undefined, fn: (transaction: SpannerReadWriteTransaction) => Promise<T>): Promise<T>;
|
|
328
|
-
/**
|
|
329
|
-
* Runs the given "read-only" function on a transaction. If a transaction is not passed, a new
|
|
330
|
-
* {@link SpannerReadOnlyTransaction} is created instead.
|
|
331
|
-
*
|
|
332
|
-
* @param transaction The transaction to use. If `undefined`, a new {@link SpannerReadOnlyTransaction} is created.
|
|
333
|
-
* @param fn The function to run on the transaction.
|
|
334
|
-
* @returns The result of the function.
|
|
335
|
-
*/
|
|
336
|
-
runInExistingOrNewReadOnlyTransaction<T>(transaction: SpannerReadOnlyTransaction | undefined, fn: SnapshotFunction<T>): Promise<T>;
|
|
337
333
|
}
|
|
338
334
|
export {};
|
|
@@ -133,7 +133,7 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
133
133
|
}
|
|
134
134
|
const { tableName, columns: allColumns, primaryKeyColumns, softDeleteColumn, } = this.tableCache.getMetadata(entityType);
|
|
135
135
|
const columns = options.columns ?? (options.index ? primaryKeyColumns : allColumns);
|
|
136
|
-
return await this.
|
|
136
|
+
return await this.snapshot({ transaction: options.transaction }, async (transaction) => {
|
|
137
137
|
const [rows] = await transaction.read(tableName, {
|
|
138
138
|
keys: [key],
|
|
139
139
|
columns,
|
|
@@ -201,16 +201,15 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
201
201
|
}
|
|
202
202
|
return entity;
|
|
203
203
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
* @param runFn The function to run in the transaction.
|
|
210
|
-
* @returns The return value of the function.
|
|
211
|
-
*/
|
|
212
|
-
async transaction(runFn) {
|
|
204
|
+
async transaction(optionsOrRunFn, runFn) {
|
|
205
|
+
const options = runFn
|
|
206
|
+
? optionsOrRunFn
|
|
207
|
+
: {};
|
|
208
|
+
runFn ??= optionsOrRunFn;
|
|
213
209
|
try {
|
|
210
|
+
if (options.transaction) {
|
|
211
|
+
return await runFn(options.transaction);
|
|
212
|
+
}
|
|
214
213
|
return await this.database.runTransactionAsync(async (transaction) => {
|
|
215
214
|
try {
|
|
216
215
|
const result = await runFn(transaction);
|
|
@@ -238,14 +237,19 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
238
237
|
}
|
|
239
238
|
}
|
|
240
239
|
async snapshot(optionsOrRunFn, runFn) {
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
: runFn;
|
|
244
|
-
const options = typeof optionsOrRunFn === 'object' ? optionsOrRunFn : {};
|
|
240
|
+
const options = runFn ? optionsOrRunFn : {};
|
|
241
|
+
runFn ??= optionsOrRunFn;
|
|
245
242
|
let snapshot;
|
|
246
243
|
try {
|
|
247
|
-
|
|
248
|
-
|
|
244
|
+
let transaction;
|
|
245
|
+
if ('transaction' in options) {
|
|
246
|
+
transaction = options.transaction;
|
|
247
|
+
}
|
|
248
|
+
if (!transaction) {
|
|
249
|
+
[snapshot] = await this.database.getSnapshot('timestampBounds' in options ? options.timestampBounds : undefined);
|
|
250
|
+
transaction = snapshot;
|
|
251
|
+
}
|
|
252
|
+
return await runFn(transaction);
|
|
249
253
|
}
|
|
250
254
|
catch (error) {
|
|
251
255
|
throw convertSpannerToEntityError(error) ?? error;
|
|
@@ -262,7 +266,7 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
262
266
|
*/
|
|
263
267
|
async clear(entityType, options = {}) {
|
|
264
268
|
const { quotedTableName } = this.tableCache.getMetadata(entityType);
|
|
265
|
-
await this.
|
|
269
|
+
await this.transaction(options, (transaction) => transaction.runUpdate({
|
|
266
270
|
sql: `DELETE FROM ${quotedTableName} WHERE TRUE`,
|
|
267
271
|
}));
|
|
268
272
|
}
|
|
@@ -272,7 +276,7 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
272
276
|
: {};
|
|
273
277
|
const sqlStatement = statement ?? optionsOrStatement;
|
|
274
278
|
const { entityType } = options;
|
|
275
|
-
return await this.
|
|
279
|
+
return await this.snapshot({ transaction: options.transaction }, async (transaction) => {
|
|
276
280
|
const [rows] = await transaction.run({
|
|
277
281
|
...sqlStatement,
|
|
278
282
|
json: true,
|
|
@@ -349,7 +353,7 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
349
353
|
*/
|
|
350
354
|
async insert(entity, options = {}) {
|
|
351
355
|
const objs = this.entitiesToSpannerObjects(entity);
|
|
352
|
-
await this.
|
|
356
|
+
await this.transaction(options, async (transaction) => Object.entries(objs).forEach(([tableName, objs]) => transaction.insert(tableName, objs)));
|
|
353
357
|
}
|
|
354
358
|
/**
|
|
355
359
|
* Replaces the given entities in the database.
|
|
@@ -364,7 +368,7 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
364
368
|
*/
|
|
365
369
|
async replace(entity, options = {}) {
|
|
366
370
|
const objs = this.entitiesToSpannerObjects(entity);
|
|
367
|
-
await this.
|
|
371
|
+
await this.transaction(options, async (transaction) => Object.entries(objs).forEach(([tableName, objs]) => transaction.replace(tableName, objs)));
|
|
368
372
|
}
|
|
369
373
|
/**
|
|
370
374
|
* Updates the given entity in the database.
|
|
@@ -386,7 +390,7 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
386
390
|
async update(entityType, update, options = {}) {
|
|
387
391
|
const primaryKey = this.getPrimaryKey(update, entityType);
|
|
388
392
|
const { tableName } = this.tableCache.getMetadata(entityType);
|
|
389
|
-
return await this.
|
|
393
|
+
return await this.transaction({ transaction: options.transaction }, async (transaction) => {
|
|
390
394
|
const existingEntity = await this.findOneByKey(entityType, primaryKey, {
|
|
391
395
|
transaction,
|
|
392
396
|
includeSoftDeletes: options.includeSoftDeletes,
|
|
@@ -427,7 +431,7 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
427
431
|
key = [key];
|
|
428
432
|
}
|
|
429
433
|
const { tableName } = this.tableCache.getMetadata(entityType);
|
|
430
|
-
return await this.
|
|
434
|
+
return await this.transaction({ transaction: options.transaction }, async (transaction) => {
|
|
431
435
|
const existingEntity = await this.findOneByKeyOrFail(entityType, key, {
|
|
432
436
|
transaction,
|
|
433
437
|
includeSoftDeletes: options.includeSoftDeletes,
|
|
@@ -439,44 +443,6 @@ let SpannerEntityManager = class SpannerEntityManager {
|
|
|
439
443
|
return existingEntity;
|
|
440
444
|
});
|
|
441
445
|
}
|
|
442
|
-
/**
|
|
443
|
-
* Runs the given "read-write" function on a transaction. If a transaction is not passed, a new
|
|
444
|
-
* {@link SpannerReadWriteTransaction} is created instead.
|
|
445
|
-
*
|
|
446
|
-
* @param transaction The transaction to use. If `undefined`, a new transaction is created.
|
|
447
|
-
* @param fn The function to run on the transaction.
|
|
448
|
-
* @returns The result of the function.
|
|
449
|
-
*/
|
|
450
|
-
async runInExistingOrNewTransaction(transaction, fn) {
|
|
451
|
-
if (transaction) {
|
|
452
|
-
try {
|
|
453
|
-
return await fn(transaction);
|
|
454
|
-
}
|
|
455
|
-
catch (error) {
|
|
456
|
-
throw convertSpannerToEntityError(error) ?? error;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return this.transaction(fn);
|
|
460
|
-
}
|
|
461
|
-
/**
|
|
462
|
-
* Runs the given "read-only" function on a transaction. If a transaction is not passed, a new
|
|
463
|
-
* {@link SpannerReadOnlyTransaction} is created instead.
|
|
464
|
-
*
|
|
465
|
-
* @param transaction The transaction to use. If `undefined`, a new {@link SpannerReadOnlyTransaction} is created.
|
|
466
|
-
* @param fn The function to run on the transaction.
|
|
467
|
-
* @returns The result of the function.
|
|
468
|
-
*/
|
|
469
|
-
async runInExistingOrNewReadOnlyTransaction(transaction, fn) {
|
|
470
|
-
if (transaction) {
|
|
471
|
-
try {
|
|
472
|
-
return await fn(transaction);
|
|
473
|
-
}
|
|
474
|
-
catch (error) {
|
|
475
|
-
throw convertSpannerToEntityError(error) ?? error;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return this.snapshot(fn);
|
|
479
|
-
}
|
|
480
446
|
};
|
|
481
447
|
SpannerEntityManager = __decorate([
|
|
482
448
|
Injectable(),
|
|
@@ -28,9 +28,6 @@ export class SpannerTableCache {
|
|
|
28
28
|
if (softDeleteColumns.length > 1) {
|
|
29
29
|
throw new InvalidEntityDefinitionError(entityType, `Only one column can be marked as soft delete.`);
|
|
30
30
|
}
|
|
31
|
-
if (softDeleteColumns[0]?.nestedType) {
|
|
32
|
-
throw new InvalidEntityDefinitionError(entityType, `Soft delete columns cannot be nested.`);
|
|
33
|
-
}
|
|
34
31
|
const softDeleteColumn = softDeleteColumns[0]?.name ?? null;
|
|
35
32
|
const columns = getSpannerColumns(entityType);
|
|
36
33
|
const quotedColumns = columns.map((c) => `\`${c}\``).join(', ');
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import type { NestJsModuleOverrider } from '@causa/runtime/nestjs/testing';
|
|
1
|
+
import type { Fixture, NestJsModuleOverrider } from '@causa/runtime/nestjs/testing';
|
|
2
2
|
import { Database, Instance, Spanner } from '@google-cloud/spanner';
|
|
3
|
+
import type { Type } from '@nestjs/common';
|
|
4
|
+
/**
|
|
5
|
+
* Parameters for creating a new test database.
|
|
6
|
+
*/
|
|
7
|
+
type CreateDatabaseParameters = Pick<SpannerFixture, 'name' | 'sourceDatabaseName' | 'spanner' | 'instance'>;
|
|
3
8
|
/**
|
|
4
9
|
* Creates a new database.
|
|
5
10
|
* This will destroy the existing database if it exists.
|
|
@@ -7,32 +12,46 @@ import { Database, Instance, Spanner } from '@google-cloud/spanner';
|
|
|
7
12
|
* @param options Options when creating the database.
|
|
8
13
|
* @returns The database object.
|
|
9
14
|
*/
|
|
10
|
-
export declare function createDatabase(options?:
|
|
15
|
+
export declare function createDatabase(options?: Partial<CreateDatabaseParameters>): Promise<Database>;
|
|
16
|
+
/**
|
|
17
|
+
* A {@link Fixture} that creates a temporary Spanner database and injects it into the NestJS application.
|
|
18
|
+
* The specified tables will be cleared after each test.
|
|
19
|
+
*/
|
|
20
|
+
export declare class SpannerFixture implements Fixture {
|
|
11
21
|
/**
|
|
12
|
-
* The name of the database
|
|
22
|
+
* The name of the temporary database.
|
|
13
23
|
*/
|
|
14
|
-
name
|
|
24
|
+
readonly name: string;
|
|
15
25
|
/**
|
|
16
26
|
* If `sourceDatabaseName` is provided, its DDL will be copied into the new database, otherwise it will try to copy
|
|
17
27
|
* the DDL from `process.env.SPANNER_DATABASE`.
|
|
18
28
|
* If `null`, no schema will be set on the created database.
|
|
19
29
|
*/
|
|
20
|
-
sourceDatabaseName
|
|
30
|
+
readonly sourceDatabaseName: string | null;
|
|
21
31
|
/**
|
|
22
|
-
* The
|
|
32
|
+
* The Spanner client to use for tests.
|
|
33
|
+
*/
|
|
34
|
+
readonly spanner: Spanner;
|
|
35
|
+
/**
|
|
36
|
+
* The Spanner instance to use for tests.
|
|
23
37
|
* By default, a new Spanner client will be created using the `SPANNER_INSTANCE` environment variable.
|
|
24
38
|
*/
|
|
25
|
-
instance
|
|
39
|
+
readonly instance: Instance;
|
|
26
40
|
/**
|
|
27
|
-
*
|
|
28
|
-
* If `instance` is provided, this will be ignored.
|
|
41
|
+
* Types of entities (Spanner tables) to clear.
|
|
29
42
|
*/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
readonly types: Type[];
|
|
44
|
+
/**
|
|
45
|
+
* The {@link SpannerEntityManager} used to clear tables.
|
|
46
|
+
*/
|
|
47
|
+
private entityManager;
|
|
48
|
+
/**
|
|
49
|
+
* The temporary test database created by this fixture.
|
|
50
|
+
*/
|
|
51
|
+
private database;
|
|
52
|
+
constructor(options?: Partial<CreateDatabaseParameters> & Partial<Pick<SpannerFixture, 'types'>>);
|
|
53
|
+
init(): Promise<NestJsModuleOverrider>;
|
|
54
|
+
clear(): Promise<void>;
|
|
55
|
+
delete(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
export {};
|