@nymphjs/nymph 1.0.0-beta.121 → 1.0.0-beta.122
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/CHANGELOG.md +13 -0
- package/README.md +12 -4
- package/dist/Entity.d.ts +4 -3
- package/dist/Entity.js +33 -6
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +19 -12
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +19 -15
- package/dist/decorators/index.d.ts +1 -0
- package/dist/decorators/index.js +2 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/transactional.d.ts +12 -0
- package/dist/decorators/transactional.js +45 -0
- package/dist/decorators/transactional.js.map +1 -0
- package/dist/driver/NymphDriver.js +14 -6
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/testArtifacts.js +115 -36
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/MethodFailedError.d.ts +3 -0
- package/dist/errors/MethodFailedError.js +7 -0
- package/dist/errors/MethodFailedError.js.map +1 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +2 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/transaction.d.ts +15 -0
- package/dist/helpers/transaction.js +47 -0
- package/dist/helpers/transaction.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +2 -0
- package/dist/testArtifacts.js +113 -34
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.js +2 -2
- package/dist/testMocks.js.map +1 -1
- package/package.json +14 -5
- package/src/Entity.test.ts +19 -12
- package/src/Entity.ts +34 -9
- package/src/Entity.types.ts +19 -15
- package/src/decorators/index.ts +1 -0
- package/src/decorators/transactional.ts +63 -0
- package/src/driver/NymphDriver.ts +18 -6
- package/src/driver/testArtifacts.ts +146 -36
- package/src/errors/MethodFailedError.ts +6 -0
- package/src/errors/index.ts +2 -0
- package/src/helpers/index.ts +1 -0
- package/src/helpers/transaction.ts +65 -0
- package/src/index.ts +2 -0
- package/src/testArtifacts.ts +49 -0
- package/src/testMocks.ts +4 -2
|
@@ -5,7 +5,10 @@ import { strtotime } from 'locutus/php/datetime/index';
|
|
|
5
5
|
import { guid } from '@nymphjs/guid';
|
|
6
6
|
|
|
7
7
|
import type Nymph from '../Nymph.js';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
EntityUniqueConstraintError,
|
|
10
|
+
EntityInvalidDataError,
|
|
11
|
+
} from '../errors/index.js';
|
|
9
12
|
import {
|
|
10
13
|
TestBModel as TestBModelClass,
|
|
11
14
|
TestModel as TestModelClass,
|
|
@@ -56,22 +59,22 @@ export function EntitiesTest(
|
|
|
56
59
|
testEntity.number = 30;
|
|
57
60
|
testEntity.numberString = '30';
|
|
58
61
|
testEntity.timestamp = Date.now();
|
|
59
|
-
|
|
62
|
+
await testEntity.$save();
|
|
60
63
|
expect(testEntity.guid).not.toBeNull();
|
|
61
64
|
testGuid = testEntity.guid as string;
|
|
62
65
|
|
|
63
66
|
const entityReferenceTest: TestModelClass & TestModelData =
|
|
64
67
|
new TestModel();
|
|
65
|
-
entityReferenceTest.string = '
|
|
68
|
+
entityReferenceTest.string = 'ref string';
|
|
66
69
|
entityReferenceTest.timestamp = (strtotime('-2 days') || 0) * 1000;
|
|
67
|
-
|
|
70
|
+
await entityReferenceTest.$save();
|
|
68
71
|
refGuid = entityReferenceTest.guid as string;
|
|
69
72
|
testEntity.reference = entityReferenceTest;
|
|
70
73
|
testEntity.refArray = [entityReferenceTest];
|
|
71
|
-
|
|
74
|
+
await testEntity.$save();
|
|
72
75
|
|
|
73
76
|
entityReferenceTest.test = 'good';
|
|
74
|
-
|
|
77
|
+
await entityReferenceTest.$save();
|
|
75
78
|
|
|
76
79
|
// Test asynchronous getEntity.
|
|
77
80
|
testEntity = (await nymph.getEntity(
|
|
@@ -94,7 +97,7 @@ export function EntitiesTest(
|
|
|
94
97
|
testEntity.number = 100 - i;
|
|
95
98
|
testEntity.$removeTag('test');
|
|
96
99
|
testEntity.$addTag('multiTest');
|
|
97
|
-
|
|
100
|
+
await testEntity.$save();
|
|
98
101
|
// Pause for a few milliseconds so the cdate and mdate can be sorted.
|
|
99
102
|
await new Promise((res) => setTimeout(() => res(1), 5));
|
|
100
103
|
}
|
|
@@ -105,7 +108,7 @@ export function EntitiesTest(
|
|
|
105
108
|
let amodels = await nymph.getEntities({ class: TestModel });
|
|
106
109
|
expect(Array.isArray(amodels)).toEqual(true);
|
|
107
110
|
for (const cur of amodels) {
|
|
108
|
-
|
|
111
|
+
await cur.$delete();
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
amodels = await nymph.getEntities({ class: TestModel });
|
|
@@ -114,7 +117,7 @@ export function EntitiesTest(
|
|
|
114
117
|
let bmodels = await nymph.getEntities({ class: TestBModel });
|
|
115
118
|
expect(Array.isArray(bmodels)).toEqual(true);
|
|
116
119
|
for (const cur of bmodels) {
|
|
117
|
-
|
|
120
|
+
await cur.$delete();
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
bmodels = await nymph.getEntities({ class: TestBModel });
|
|
@@ -128,7 +131,13 @@ export function EntitiesTest(
|
|
|
128
131
|
it("doesn't create empty entity", async () => {
|
|
129
132
|
const testEntity = await TestEmptyModel.factory();
|
|
130
133
|
|
|
131
|
-
|
|
134
|
+
try {
|
|
135
|
+
await testEntity.$save();
|
|
136
|
+
|
|
137
|
+
throw new Error('Should have thrown.');
|
|
138
|
+
} catch (e: any) {
|
|
139
|
+
expect(e).toBeInstanceOf(EntityInvalidDataError);
|
|
140
|
+
}
|
|
132
141
|
expect(testEntity.guid).toBeNull();
|
|
133
142
|
});
|
|
134
143
|
|
|
@@ -180,7 +189,7 @@ export function EntitiesTest(
|
|
|
180
189
|
// Get the guaranteed GUID.
|
|
181
190
|
const testGuid = testEntity.$getGuaranteedGUID();
|
|
182
191
|
|
|
183
|
-
|
|
192
|
+
await testEntity.$save();
|
|
184
193
|
expect(testEntity.guid).not.toBeNull();
|
|
185
194
|
expect(testEntity.guid).toEqual(testGuid);
|
|
186
195
|
|
|
@@ -189,7 +198,7 @@ export function EntitiesTest(
|
|
|
189
198
|
expect(testEntity.$is(resultEntity)).toEqual(true);
|
|
190
199
|
|
|
191
200
|
// Delete the entity.
|
|
192
|
-
|
|
201
|
+
await testEntity.$delete();
|
|
193
202
|
});
|
|
194
203
|
|
|
195
204
|
it('create, list, and delete indexes', async () => {
|
|
@@ -324,7 +333,7 @@ export function EntitiesTest(
|
|
|
324
333
|
|
|
325
334
|
// Change a value in the test entity.
|
|
326
335
|
testEntity.string = 'bad value';
|
|
327
|
-
|
|
336
|
+
await testEntity.$save();
|
|
328
337
|
|
|
329
338
|
// Rollback the transaction.
|
|
330
339
|
await transaction.rollback('test');
|
|
@@ -344,7 +353,7 @@ export function EntitiesTest(
|
|
|
344
353
|
expect(await transaction.inTransaction()).toEqual(true);
|
|
345
354
|
|
|
346
355
|
// Delete the entity.
|
|
347
|
-
|
|
356
|
+
await testEntity.$delete();
|
|
348
357
|
const resultEntity = await transaction.getEntity(
|
|
349
358
|
{ class: TestModel },
|
|
350
359
|
testGuid,
|
|
@@ -374,7 +383,7 @@ export function EntitiesTest(
|
|
|
374
383
|
|
|
375
384
|
// Make a change.
|
|
376
385
|
testEntity.string = 'fish';
|
|
377
|
-
|
|
386
|
+
await testEntity.$save();
|
|
378
387
|
|
|
379
388
|
// Commit the transaction.
|
|
380
389
|
await transaction.commit('test.1');
|
|
@@ -389,11 +398,103 @@ export function EntitiesTest(
|
|
|
389
398
|
|
|
390
399
|
// Finally, change it back.
|
|
391
400
|
testEntity.string = 'test';
|
|
392
|
-
|
|
401
|
+
await testEntity.$save();
|
|
393
402
|
|
|
394
403
|
// TODO: nested transactions
|
|
395
404
|
});
|
|
396
405
|
|
|
406
|
+
it('transactional decorator', async () => {
|
|
407
|
+
await createTestEntities();
|
|
408
|
+
|
|
409
|
+
if (testEntity.reference == null) {
|
|
410
|
+
throw new Error('Reference is null.');
|
|
411
|
+
}
|
|
412
|
+
await testEntity.reference.$wake();
|
|
413
|
+
|
|
414
|
+
expect(testEntity.string).toEqual('test');
|
|
415
|
+
expect(testEntity.reference.string).toEqual('ref string');
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
await testEntity.$testTransactionalDecorator(true);
|
|
419
|
+
|
|
420
|
+
throw Error('Should have thrown an error.');
|
|
421
|
+
} catch (e: any) {
|
|
422
|
+
expect(e.message).toEqual('An error.');
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (!(await testEntity.$refresh())) {
|
|
426
|
+
throw new Error("Entity couldn't be refreshed.");
|
|
427
|
+
}
|
|
428
|
+
await testEntity.reference.$wake();
|
|
429
|
+
|
|
430
|
+
expect(testEntity.string).toEqual('test');
|
|
431
|
+
expect(testEntity.reference.string).toEqual('ref string');
|
|
432
|
+
|
|
433
|
+
expect(await testEntity.$testTransactionalDecorator(false)).toEqual(
|
|
434
|
+
'Success.',
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
if (!(await testEntity.$refresh())) {
|
|
438
|
+
throw new Error("Entity couldn't be refreshed.");
|
|
439
|
+
}
|
|
440
|
+
await testEntity.reference.$wake();
|
|
441
|
+
|
|
442
|
+
expect(testEntity.string).toEqual('this has been changed');
|
|
443
|
+
expect(testEntity.reference.string).toEqual('this has been changed too');
|
|
444
|
+
|
|
445
|
+
// Reset back to original values.
|
|
446
|
+
testEntity.reference.string = 'ref string';
|
|
447
|
+
await testEntity.reference.$save();
|
|
448
|
+
testEntity.string = 'test';
|
|
449
|
+
await testEntity.$save();
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('transaction helper', async () => {
|
|
453
|
+
await createTestEntities();
|
|
454
|
+
|
|
455
|
+
if (testEntity.reference == null) {
|
|
456
|
+
throw new Error('Reference is null.');
|
|
457
|
+
}
|
|
458
|
+
await testEntity.reference.$wake();
|
|
459
|
+
|
|
460
|
+
expect(testEntity.string).toEqual('test');
|
|
461
|
+
expect(testEntity.reference.string).toEqual('ref string');
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
await testEntity.$testTransactionHelper(true);
|
|
465
|
+
|
|
466
|
+
throw Error('Should have thrown an error.');
|
|
467
|
+
} catch (e: any) {
|
|
468
|
+
expect(e.message).toEqual('An error.');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (!(await testEntity.$refresh())) {
|
|
472
|
+
throw new Error("Entity couldn't be refreshed.");
|
|
473
|
+
}
|
|
474
|
+
await testEntity.reference.$wake();
|
|
475
|
+
|
|
476
|
+
expect(testEntity.string).toEqual('test');
|
|
477
|
+
expect(testEntity.reference.string).toEqual('ref string');
|
|
478
|
+
|
|
479
|
+
expect(await testEntity.$testTransactionHelper(false)).toEqual(
|
|
480
|
+
'Success.',
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
if (!(await testEntity.$refresh())) {
|
|
484
|
+
throw new Error("Entity couldn't be refreshed.");
|
|
485
|
+
}
|
|
486
|
+
await testEntity.reference.$wake();
|
|
487
|
+
|
|
488
|
+
expect(testEntity.string).toEqual('this has been changed');
|
|
489
|
+
expect(testEntity.reference.string).toEqual('this has been changed too');
|
|
490
|
+
|
|
491
|
+
// Reset back to original values.
|
|
492
|
+
testEntity.reference.string = 'ref string';
|
|
493
|
+
await testEntity.reference.$save();
|
|
494
|
+
testEntity.string = 'test';
|
|
495
|
+
await testEntity.$save();
|
|
496
|
+
});
|
|
497
|
+
|
|
397
498
|
it('options', async () => {
|
|
398
499
|
await createTestEntities();
|
|
399
500
|
|
|
@@ -674,7 +775,7 @@ export function EntitiesTest(
|
|
|
674
775
|
// Retrieving entity by !equal...
|
|
675
776
|
const resultEntity = await nymph.getEntities(
|
|
676
777
|
{ class: TestModel },
|
|
677
|
-
{ type: '&', tag: 'test', '!equal': ['string', '
|
|
778
|
+
{ type: '&', tag: 'test', '!equal': ['string', 'ref string'] },
|
|
678
779
|
);
|
|
679
780
|
expect(testEntity.$inArray(resultEntity)).toEqual(true);
|
|
680
781
|
expect(referenceEntity.$inArray(resultEntity)).toEqual(false);
|
|
@@ -700,7 +801,7 @@ export function EntitiesTest(
|
|
|
700
801
|
// Retrieving entity by !like...
|
|
701
802
|
const resultEntity = await nymph.getEntities(
|
|
702
803
|
{ class: TestModel },
|
|
703
|
-
{ type: '&', tag: 'test', '!like': ['string', '
|
|
804
|
+
{ type: '&', tag: 'test', '!like': ['string', 'r_f%'] },
|
|
704
805
|
);
|
|
705
806
|
expect(testEntity.$inArray(resultEntity)).toEqual(true);
|
|
706
807
|
expect(referenceEntity.$inArray(resultEntity)).toEqual(false);
|
|
@@ -726,7 +827,7 @@ export function EntitiesTest(
|
|
|
726
827
|
// Retrieving entity by !ilike...
|
|
727
828
|
const resultEntity = await nymph.getEntities(
|
|
728
829
|
{ class: TestModel },
|
|
729
|
-
{ type: '&', tag: 'test', '!ilike': ['string', '
|
|
830
|
+
{ type: '&', tag: 'test', '!ilike': ['string', 'r_f%'] },
|
|
730
831
|
);
|
|
731
832
|
expect(testEntity.$inArray(resultEntity)).toEqual(true);
|
|
732
833
|
expect(referenceEntity.$inArray(resultEntity)).toEqual(false);
|
|
@@ -2130,14 +2231,19 @@ export function EntitiesTest(
|
|
|
2130
2231
|
await createMultipleTestEntities();
|
|
2131
2232
|
|
|
2132
2233
|
for (const sort of ['cdate', 'mdate'] as ('cdate' | 'mdate')[]) {
|
|
2234
|
+
// These ones might have the same dates (saved at exactly the same time)
|
|
2235
|
+
// so we use >= and <= instead of > and <.
|
|
2133
2236
|
// Retrieving entities sorted...
|
|
2134
2237
|
let resultEntities = await nymph.getEntities({
|
|
2135
2238
|
class: TestModel,
|
|
2136
2239
|
sort,
|
|
2137
2240
|
});
|
|
2138
2241
|
expect(resultEntities.length).toBeGreaterThan(100);
|
|
2242
|
+
expect(resultEntities[resultEntities.length - 1][sort]).toBeGreaterThan(
|
|
2243
|
+
resultEntities[0][sort] ?? 0,
|
|
2244
|
+
);
|
|
2139
2245
|
for (let i = 0; i < resultEntities.length - 1; i++) {
|
|
2140
|
-
expect(resultEntities[i + 1][sort]).
|
|
2246
|
+
expect(resultEntities[i + 1][sort]).toBeGreaterThanOrEqual(
|
|
2141
2247
|
resultEntities[i][sort] ?? 0,
|
|
2142
2248
|
);
|
|
2143
2249
|
}
|
|
@@ -2160,13 +2266,17 @@ export function EntitiesTest(
|
|
|
2160
2266
|
reverse: true,
|
|
2161
2267
|
});
|
|
2162
2268
|
expect(resultEntities.length).toBeGreaterThan(100);
|
|
2269
|
+
expect(resultEntities[resultEntities.length - 1][sort]).toBeLessThan(
|
|
2270
|
+
resultEntities[0][sort] ?? 0,
|
|
2271
|
+
);
|
|
2163
2272
|
for (let i = 0; i < resultEntities.length - 1; i++) {
|
|
2164
|
-
expect(resultEntities[i + 1][sort]).
|
|
2273
|
+
expect(resultEntities[i + 1][sort]).toBeLessThanOrEqual(
|
|
2165
2274
|
resultEntities[i][sort] ?? 0,
|
|
2166
2275
|
);
|
|
2167
2276
|
}
|
|
2168
2277
|
|
|
2169
2278
|
// And again with other selectors.
|
|
2279
|
+
// These ones should have different dates every time.
|
|
2170
2280
|
// Retrieving entities sorted...
|
|
2171
2281
|
resultEntities = await nymph.getEntities(
|
|
2172
2282
|
{ class: TestModel, sort },
|
|
@@ -2269,7 +2379,7 @@ export function EntitiesTest(
|
|
|
2269
2379
|
|
|
2270
2380
|
// Deleting referenced entities...
|
|
2271
2381
|
await testEntity.reference?.$wake();
|
|
2272
|
-
|
|
2382
|
+
await testEntity.reference?.$delete();
|
|
2273
2383
|
await Promise.all(testEntity.refArray?.map((e) => e.$wake()) || []);
|
|
2274
2384
|
expect(testEntity.reference?.guid).toBeNull();
|
|
2275
2385
|
});
|
|
@@ -2278,7 +2388,7 @@ export function EntitiesTest(
|
|
|
2278
2388
|
await createTestEntities();
|
|
2279
2389
|
|
|
2280
2390
|
// Deleting entity...
|
|
2281
|
-
|
|
2391
|
+
await testEntity.$delete();
|
|
2282
2392
|
expect(testEntity.guid).toBeNull();
|
|
2283
2393
|
|
|
2284
2394
|
const entity = await nymph.getEntity(
|
|
@@ -2296,7 +2406,7 @@ export function EntitiesTest(
|
|
|
2296
2406
|
uniqueEntityA.uniques = ['test a'];
|
|
2297
2407
|
|
|
2298
2408
|
// Saving entity...
|
|
2299
|
-
|
|
2409
|
+
await uniqueEntityA.$save();
|
|
2300
2410
|
expect(uniqueEntityA.guid).not.toBeNull();
|
|
2301
2411
|
|
|
2302
2412
|
// Creating entity...
|
|
@@ -2305,13 +2415,13 @@ export function EntitiesTest(
|
|
|
2305
2415
|
uniqueEntityB.uniques = ['test b'];
|
|
2306
2416
|
|
|
2307
2417
|
// Saving entity...
|
|
2308
|
-
|
|
2418
|
+
await uniqueEntityB.$save();
|
|
2309
2419
|
expect(uniqueEntityB.guid).not.toBeNull();
|
|
2310
2420
|
|
|
2311
2421
|
expect(uniqueEntityA.guid).not.toEqual(uniqueEntityB.guid);
|
|
2312
2422
|
|
|
2313
|
-
|
|
2314
|
-
|
|
2423
|
+
await uniqueEntityA.$delete();
|
|
2424
|
+
await uniqueEntityB.$delete();
|
|
2315
2425
|
});
|
|
2316
2426
|
|
|
2317
2427
|
it('throws on duplicate unique strings', async () => {
|
|
@@ -2321,7 +2431,7 @@ export function EntitiesTest(
|
|
|
2321
2431
|
uniqueEntityA.uniques = ['test a'];
|
|
2322
2432
|
|
|
2323
2433
|
// Saving entity...
|
|
2324
|
-
|
|
2434
|
+
await uniqueEntityA.$save();
|
|
2325
2435
|
expect(uniqueEntityA.guid).not.toBeNull();
|
|
2326
2436
|
|
|
2327
2437
|
// Creating entity...
|
|
@@ -2339,7 +2449,7 @@ export function EntitiesTest(
|
|
|
2339
2449
|
}
|
|
2340
2450
|
expect(uniqueEntityB.guid).toBeNull();
|
|
2341
2451
|
|
|
2342
|
-
|
|
2452
|
+
await uniqueEntityA.$delete();
|
|
2343
2453
|
});
|
|
2344
2454
|
|
|
2345
2455
|
it('allows duplicate unique string if first transaction is rolled back', async () => {
|
|
@@ -2351,7 +2461,7 @@ export function EntitiesTest(
|
|
|
2351
2461
|
uniqueEntityA.uniques = ['test a'];
|
|
2352
2462
|
|
|
2353
2463
|
// Saving entity...
|
|
2354
|
-
|
|
2464
|
+
await uniqueEntityA.$save();
|
|
2355
2465
|
expect(uniqueEntityA.guid).not.toBeNull();
|
|
2356
2466
|
|
|
2357
2467
|
// Rolling back transaction...
|
|
@@ -2365,7 +2475,7 @@ export function EntitiesTest(
|
|
|
2365
2475
|
uniqueEntityB.uniques = ['test a'];
|
|
2366
2476
|
|
|
2367
2477
|
// Saving entity...
|
|
2368
|
-
|
|
2478
|
+
await uniqueEntityB.$save();
|
|
2369
2479
|
expect(uniqueEntityB.guid).not.toBeNull();
|
|
2370
2480
|
|
|
2371
2481
|
expect(uniqueEntityA.guid).not.toEqual(uniqueEntityB.guid);
|
|
@@ -2385,7 +2495,7 @@ export function EntitiesTest(
|
|
|
2385
2495
|
expect(uniqueEntityBCheck).not.toBeNull();
|
|
2386
2496
|
|
|
2387
2497
|
if (uniqueEntityBCheck != null) {
|
|
2388
|
-
|
|
2498
|
+
await uniqueEntityBCheck.$delete();
|
|
2389
2499
|
}
|
|
2390
2500
|
});
|
|
2391
2501
|
});
|
|
@@ -2448,7 +2558,7 @@ export function ExportImportTest(
|
|
|
2448
2558
|
let all = await nymph.getEntities({ class: TestModel });
|
|
2449
2559
|
expect(Array.isArray(all)).toEqual(true);
|
|
2450
2560
|
for (const cur of all) {
|
|
2451
|
-
|
|
2561
|
+
await cur.$delete();
|
|
2452
2562
|
}
|
|
2453
2563
|
|
|
2454
2564
|
all = await nymph.getEntities({ class: TestModel });
|
|
@@ -2457,7 +2567,7 @@ export function ExportImportTest(
|
|
|
2457
2567
|
all = await nymph.getEntities({ class: TestBModel });
|
|
2458
2568
|
expect(Array.isArray(all)).toEqual(true);
|
|
2459
2569
|
for (const cur of all) {
|
|
2460
|
-
|
|
2570
|
+
await cur.$delete();
|
|
2461
2571
|
}
|
|
2462
2572
|
|
|
2463
2573
|
all = await nymph.getEntities({ class: TestBModel });
|
|
@@ -2547,11 +2657,11 @@ export function ExportImportTest(
|
|
|
2547
2657
|
entityReferenceTest.string = 'another';
|
|
2548
2658
|
entityReferenceTest.index = i + 'b';
|
|
2549
2659
|
|
|
2550
|
-
|
|
2660
|
+
await entityReferenceTest.$save();
|
|
2551
2661
|
testEntity.reference = entityReferenceTest;
|
|
2552
2662
|
testEntity.refArray = [entityReferenceTest];
|
|
2553
2663
|
|
|
2554
|
-
|
|
2664
|
+
await testEntity.$save();
|
|
2555
2665
|
}
|
|
2556
2666
|
|
|
2557
2667
|
await checkEntityDataAndCount();
|
package/src/errors/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './transaction.js';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import Nymph from '../Nymph.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Run a function inside of a transaction.
|
|
5
|
+
*
|
|
6
|
+
* @param nymph The instance of Nymph to start the transaction on.
|
|
7
|
+
* @param name The name of the transaction.
|
|
8
|
+
* @param fn The function that runs inside the transaction. It is passed the
|
|
9
|
+
* transactional instance of Nymph. If it resolves, the transaction is
|
|
10
|
+
* committed, and if it throws, the transaction is rolled back.
|
|
11
|
+
* @param finallyFn An optional function to run after the transaction is
|
|
12
|
+
* committed or rolled back. It is passed the original instance
|
|
13
|
+
* of Nymph. It SHOULD NOT throw.
|
|
14
|
+
* @returns The return value of the function.
|
|
15
|
+
*/
|
|
16
|
+
export async function transaction<T>(
|
|
17
|
+
nymph: Nymph,
|
|
18
|
+
name: string,
|
|
19
|
+
fn: (nymph: Nymph) => Promise<T>,
|
|
20
|
+
finallyFn?: (nymph: Nymph) => Promise<void>,
|
|
21
|
+
): Promise<T> {
|
|
22
|
+
nymph.config.debugInfo('nymph:transaction', `Starting transaction ${name}`);
|
|
23
|
+
const tnymph = await nymph.startTransaction(name);
|
|
24
|
+
try {
|
|
25
|
+
const result = await fn(tnymph);
|
|
26
|
+
|
|
27
|
+
nymph.config.debugInfo(
|
|
28
|
+
'nymph:transaction',
|
|
29
|
+
`Committing transaction ${name}`,
|
|
30
|
+
);
|
|
31
|
+
if (!(await tnymph.commit(name))) {
|
|
32
|
+
throw new Error('Transaction could not be committed.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (finallyFn) {
|
|
36
|
+
await finallyFn(nymph);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
} catch (e: any) {
|
|
41
|
+
try {
|
|
42
|
+
nymph.config.debugInfo(
|
|
43
|
+
'nymph:transaction',
|
|
44
|
+
`Rolling back transaction ${name}, reason: ${e.message}`,
|
|
45
|
+
);
|
|
46
|
+
await tnymph.rollback(name);
|
|
47
|
+
} catch (e: any) {
|
|
48
|
+
nymph.config.debugError(
|
|
49
|
+
'nymph:transaction',
|
|
50
|
+
`Rollback of transaction ${name} failed, reason: ${e.message}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (finallyFn) {
|
|
54
|
+
try {
|
|
55
|
+
await finallyFn(nymph);
|
|
56
|
+
} catch (e: any) {
|
|
57
|
+
nymph.config.debugError(
|
|
58
|
+
'nymph:transaction',
|
|
59
|
+
`Finally function of transaction ${name} threw error: ${e.message}, this is a mistake and should be fixed, finally functions should never throw`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
throw e;
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/index.ts
CHANGED
package/src/testArtifacts.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import Entity from './Entity.js';
|
|
2
|
+
import { transactional } from './decorators/index.js';
|
|
3
|
+
import { transaction } from './helpers/index.js';
|
|
2
4
|
|
|
3
5
|
export type TestModelData = {
|
|
4
6
|
name?: string;
|
|
@@ -67,6 +69,53 @@ export class TestModel extends Entity<TestModelData> {
|
|
|
67
69
|
this.$protectedTags = $protectedTags;
|
|
68
70
|
};
|
|
69
71
|
}
|
|
72
|
+
|
|
73
|
+
@transactional
|
|
74
|
+
public async $testTransactionalDecorator(throwError: boolean) {
|
|
75
|
+
this.$data.string = 'this has been changed';
|
|
76
|
+
|
|
77
|
+
if (this.$data.reference) {
|
|
78
|
+
await this.$data.reference.$wake();
|
|
79
|
+
this.$data.reference.string = 'this has been changed too';
|
|
80
|
+
await this.$data.reference.$save();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await this.$save();
|
|
84
|
+
|
|
85
|
+
if (throwError) {
|
|
86
|
+
throw Error('An error.');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return 'Success.';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public async $testTransactionHelper(throwError: boolean) {
|
|
93
|
+
return await transaction(
|
|
94
|
+
this.$nymph,
|
|
95
|
+
'test-transaction',
|
|
96
|
+
async (nymph) => {
|
|
97
|
+
this.$setNymph(nymph);
|
|
98
|
+
this.$data.string = 'this has been changed';
|
|
99
|
+
|
|
100
|
+
if (this.$data.reference) {
|
|
101
|
+
await this.$data.reference.$wake();
|
|
102
|
+
this.$data.reference.string = 'this has been changed too';
|
|
103
|
+
await this.$data.reference.$save();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
await this.$save();
|
|
107
|
+
|
|
108
|
+
if (throwError) {
|
|
109
|
+
throw Error('An error.');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return 'Success.';
|
|
113
|
+
},
|
|
114
|
+
async (nymph) => {
|
|
115
|
+
this.$setNymph(nymph);
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
}
|
|
70
119
|
}
|
|
71
120
|
|
|
72
121
|
/**
|
package/src/testMocks.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
EntityJson,
|
|
9
9
|
} from './Entity.types.js';
|
|
10
10
|
import { Options, Selector } from './Nymph.types.js';
|
|
11
|
-
import { ClassNotAvailableError } from './errors/index.js';
|
|
11
|
+
import { ClassNotAvailableError, EntityConflictError } from './errors/index.js';
|
|
12
12
|
import Entity from './Entity.js';
|
|
13
13
|
|
|
14
14
|
const entities: { [k: string]: EntityJson } = {};
|
|
@@ -163,7 +163,9 @@ export class MockNymph {
|
|
|
163
163
|
entity.guid in entities &&
|
|
164
164
|
(entity.mdate ?? 0) < (entities[entity.guid].mdate ?? 0)
|
|
165
165
|
) {
|
|
166
|
-
|
|
166
|
+
throw new EntityConflictError(
|
|
167
|
+
"Entity either doesn't exist or is newer in the DB.",
|
|
168
|
+
);
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
entity.mdate = Date.now();
|