@nymphjs/nymph 1.0.0-beta.121 → 1.0.0-beta.123
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 +19 -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
package/dist/testArtifacts.js
CHANGED
|
@@ -1,41 +1,120 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
1
35
|
import Entity from './Entity.js';
|
|
36
|
+
import { transactional } from './decorators/index.js';
|
|
37
|
+
import { transaction } from './helpers/index.js';
|
|
2
38
|
/**
|
|
3
39
|
* This class is a test class that extends the Entity class.
|
|
4
40
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
$
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
let TestModel = (() => {
|
|
42
|
+
let _classSuper = Entity;
|
|
43
|
+
let _instanceExtraInitializers = [];
|
|
44
|
+
let _$testTransactionalDecorator_decorators;
|
|
45
|
+
return class TestModel extends _classSuper {
|
|
46
|
+
static {
|
|
47
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
48
|
+
_$testTransactionalDecorator_decorators = [transactional];
|
|
49
|
+
__esDecorate(this, null, _$testTransactionalDecorator_decorators, { kind: "method", name: "$testTransactionalDecorator", static: false, private: false, access: { has: obj => "$testTransactionalDecorator" in obj, get: obj => obj.$testTransactionalDecorator }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
50
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
51
|
+
}
|
|
52
|
+
static ETYPE = 'test_model';
|
|
53
|
+
static class = 'TestModel';
|
|
54
|
+
$privateData = (__runInitializers(this, _instanceExtraInitializers), ['boolean']);
|
|
55
|
+
static searchRestrictedData = ['fish'];
|
|
56
|
+
$allowlistData = ['string', 'array', 'mdate'];
|
|
57
|
+
$protectedTags = ['test', 'notag'];
|
|
58
|
+
$allowlistTags = ['newtag'];
|
|
59
|
+
constructor() {
|
|
60
|
+
super();
|
|
61
|
+
this.$addTag('test');
|
|
62
|
+
this.$data.boolean = true;
|
|
63
|
+
this.$data.uniques = [];
|
|
64
|
+
}
|
|
65
|
+
async $getUniques() {
|
|
66
|
+
return this.$data.uniques ?? [];
|
|
67
|
+
}
|
|
68
|
+
$useProtectedData() {
|
|
69
|
+
const $allowlistData = this.$allowlistData;
|
|
70
|
+
const $allowlistTags = this.$allowlistTags;
|
|
71
|
+
const $protectedData = this.$protectedData;
|
|
72
|
+
const $protectedTags = this.$protectedTags;
|
|
73
|
+
delete this.$allowlistData;
|
|
74
|
+
delete this.$allowlistTags;
|
|
75
|
+
this.$protectedData = ['number'];
|
|
76
|
+
this.$protectedTags = [];
|
|
77
|
+
return () => {
|
|
78
|
+
this.$allowlistData = $allowlistData;
|
|
79
|
+
this.$allowlistTags = $allowlistTags;
|
|
80
|
+
this.$protectedData = $protectedData;
|
|
81
|
+
this.$protectedTags = $protectedTags;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async $testTransactionalDecorator(throwError) {
|
|
85
|
+
this.$data.string = 'this has been changed';
|
|
86
|
+
if (this.$data.reference) {
|
|
87
|
+
await this.$data.reference.$wake();
|
|
88
|
+
this.$data.reference.string = 'this has been changed too';
|
|
89
|
+
await this.$data.reference.$save();
|
|
90
|
+
}
|
|
91
|
+
await this.$save();
|
|
92
|
+
if (throwError) {
|
|
93
|
+
throw Error('An error.');
|
|
94
|
+
}
|
|
95
|
+
return 'Success.';
|
|
96
|
+
}
|
|
97
|
+
async $testTransactionHelper(throwError) {
|
|
98
|
+
return await transaction(this.$nymph, 'test-transaction', async (nymph) => {
|
|
99
|
+
this.$setNymph(nymph);
|
|
100
|
+
this.$data.string = 'this has been changed';
|
|
101
|
+
if (this.$data.reference) {
|
|
102
|
+
await this.$data.reference.$wake();
|
|
103
|
+
this.$data.reference.string = 'this has been changed too';
|
|
104
|
+
await this.$data.reference.$save();
|
|
105
|
+
}
|
|
106
|
+
await this.$save();
|
|
107
|
+
if (throwError) {
|
|
108
|
+
throw Error('An error.');
|
|
109
|
+
}
|
|
110
|
+
return 'Success.';
|
|
111
|
+
}, async (nymph) => {
|
|
112
|
+
this.$setNymph(nymph);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
})();
|
|
117
|
+
export { TestModel };
|
|
39
118
|
/**
|
|
40
119
|
* This class is a test class that extends the Entity class.
|
|
41
120
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testArtifacts.js","sourceRoot":"","sources":["../src/testArtifacts.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"testArtifacts.js","sourceRoot":"","sources":["../src/testArtifacts.ts"],"names":[],"mappings":"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"}
|
package/dist/testMocks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { guid } from '@nymphjs/guid';
|
|
2
|
-
import { ClassNotAvailableError } from './errors/index.js';
|
|
2
|
+
import { ClassNotAvailableError, EntityConflictError } from './errors/index.js';
|
|
3
3
|
import Entity from './Entity.js';
|
|
4
4
|
const entities = {};
|
|
5
5
|
export class MockNymphDriver {
|
|
@@ -75,7 +75,7 @@ export class MockNymph {
|
|
|
75
75
|
}
|
|
76
76
|
else if (entity.guid in entities &&
|
|
77
77
|
(entity.mdate ?? 0) < (entities[entity.guid].mdate ?? 0)) {
|
|
78
|
-
|
|
78
|
+
throw new EntityConflictError("Entity either doesn't exist or is newer in the DB.");
|
|
79
79
|
}
|
|
80
80
|
entity.mdate = Date.now();
|
|
81
81
|
const entityJson = {
|
package/dist/testMocks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testMocks.js","sourceRoot":"","sources":["../src/testMocks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAUrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"testMocks.js","sourceRoot":"","sources":["../src/testMocks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nymphjs/nymph",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0-beta.123",
|
|
4
|
+
"description": "Application Data and Access Framework for Node.js and the Browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"keywords": [
|
|
9
9
|
"nymph",
|
|
10
|
+
"nymph.js",
|
|
11
|
+
"application data framework",
|
|
12
|
+
"application data and access framework",
|
|
13
|
+
"object relational mapper",
|
|
10
14
|
"ORM",
|
|
11
|
-
"
|
|
15
|
+
"user management",
|
|
16
|
+
"group management",
|
|
17
|
+
"application framework",
|
|
18
|
+
"data framework",
|
|
19
|
+
"database abstraction",
|
|
20
|
+
"database abstraction layer"
|
|
12
21
|
],
|
|
13
22
|
"scripts": {
|
|
14
23
|
"clean": "test -d dist && rm -r dist || true",
|
|
@@ -34,7 +43,7 @@
|
|
|
34
43
|
},
|
|
35
44
|
"license": "Apache-2.0",
|
|
36
45
|
"dependencies": {
|
|
37
|
-
"@nymphjs/guid": "^1.0.0-beta.
|
|
46
|
+
"@nymphjs/guid": "^1.0.0-beta.123",
|
|
38
47
|
"@sciactive/tokenizer": "^3.4.0",
|
|
39
48
|
"debug": "^4.4.3",
|
|
40
49
|
"joi": "^18.2.5",
|
|
@@ -57,5 +66,5 @@
|
|
|
57
66
|
"ts-node": "^10.9.2",
|
|
58
67
|
"typescript": "^7.0.2"
|
|
59
68
|
},
|
|
60
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "c37dc940d7bf39e0f63158add6d0c54e7c050782"
|
|
61
70
|
}
|
package/src/Entity.test.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { jest } from '@jest/globals';
|
|
|
2
2
|
|
|
3
3
|
import { cloneDeep } from 'lodash-es';
|
|
4
4
|
|
|
5
|
+
import { EntityConflictError } from './errors/index.js';
|
|
5
6
|
import { MockNymph } from './testMocks.js';
|
|
6
7
|
import {
|
|
7
8
|
TestBModel as TestBModelClass,
|
|
@@ -44,22 +45,22 @@ describe('Entity', () => {
|
|
|
44
45
|
expect(testEntity.array).toEqual(['full', 'of', 'values', 500]);
|
|
45
46
|
expect(testEntity.number).toEqual(30);
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
await testEntity.$save();
|
|
48
49
|
expect(typeof testEntity.guid).toEqual('string');
|
|
49
50
|
|
|
50
51
|
entityReferenceTest = await TestModel.factory();
|
|
51
52
|
entityReferenceTest.string = 'wrong';
|
|
52
|
-
|
|
53
|
+
await entityReferenceTest.$save();
|
|
53
54
|
entityReferenceGuid = entityReferenceTest.guid as string;
|
|
54
55
|
testEntity.reference = entityReferenceTest;
|
|
55
56
|
testEntity.refArray = [entityReferenceTest];
|
|
56
57
|
testEntity.refObject = {
|
|
57
58
|
entity: entityReferenceTest,
|
|
58
59
|
};
|
|
59
|
-
|
|
60
|
+
await testEntity.$save();
|
|
60
61
|
|
|
61
62
|
entityReferenceTest.test = 'good';
|
|
62
|
-
|
|
63
|
+
await entityReferenceTest.$save();
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
it('comparisons work', async () => {
|
|
@@ -118,14 +119,14 @@ describe('Entity', () => {
|
|
|
118
119
|
it('refresh updates', async () => {
|
|
119
120
|
expect(testEntity.string).toEqual('test');
|
|
120
121
|
testEntity.string = 'updated';
|
|
121
|
-
|
|
122
|
+
await testEntity.$save();
|
|
122
123
|
await testEntity.$refresh();
|
|
123
|
-
|
|
124
|
+
await testEntity.$save();
|
|
124
125
|
|
|
125
126
|
const retrieve = await TestModel.factory(testEntity.guid as string);
|
|
126
127
|
expect(retrieve.string).toEqual('updated');
|
|
127
128
|
retrieve.string = 'test';
|
|
128
|
-
|
|
129
|
+
await retrieve.$save();
|
|
129
130
|
|
|
130
131
|
await testEntity.$refresh();
|
|
131
132
|
expect(testEntity.string).toEqual('test');
|
|
@@ -135,12 +136,18 @@ describe('Entity', () => {
|
|
|
135
136
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
136
137
|
|
|
137
138
|
const testEntityCopy = await TestModel.factory(testEntity.guid as string);
|
|
138
|
-
|
|
139
|
+
await testEntityCopy.$save();
|
|
139
140
|
expect(testEntityCopy.mdate ?? 0).toBeGreaterThan(testEntity.mdate ?? 0);
|
|
140
141
|
|
|
141
142
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
142
143
|
|
|
143
|
-
|
|
144
|
+
try {
|
|
145
|
+
await testEntity.$save();
|
|
146
|
+
|
|
147
|
+
throw new Error('Should have thrown.');
|
|
148
|
+
} catch (e: any) {
|
|
149
|
+
expect(e).toBeInstanceOf(EntityConflictError);
|
|
150
|
+
}
|
|
144
151
|
|
|
145
152
|
await testEntity.$refresh();
|
|
146
153
|
|
|
@@ -189,12 +196,12 @@ describe('Entity', () => {
|
|
|
189
196
|
|
|
190
197
|
// Remove all tags.
|
|
191
198
|
testEntity.$removeTag('test');
|
|
192
|
-
|
|
199
|
+
await testEntity.$save();
|
|
193
200
|
expect(await testEntity.$refresh()).toEqual(true);
|
|
194
201
|
expect(testEntity.$hasTag('test')).toEqual(false);
|
|
195
202
|
expect(testEntity.$getTags()).toEqual([]);
|
|
196
203
|
testEntity.$addTag('test');
|
|
197
|
-
|
|
204
|
+
await testEntity.$save();
|
|
198
205
|
expect(testEntity.$hasTag('test')).toEqual(true);
|
|
199
206
|
});
|
|
200
207
|
|
|
@@ -396,7 +403,7 @@ describe('Entity', () => {
|
|
|
396
403
|
// Test that an old JSON payload causes a conflict.
|
|
397
404
|
const json = JSON.stringify(testEntity);
|
|
398
405
|
|
|
399
|
-
|
|
406
|
+
await testEntity.$save();
|
|
400
407
|
|
|
401
408
|
let thrown = false;
|
|
402
409
|
let thrownName: string = '';
|
package/src/Entity.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
EntityIsSleepingReferenceError,
|
|
20
20
|
InvalidParametersError,
|
|
21
21
|
InvalidStateError,
|
|
22
|
+
MethodFailedError,
|
|
22
23
|
} from './errors/index.js';
|
|
23
24
|
import {
|
|
24
25
|
entitiesToReferences,
|
|
@@ -604,6 +605,28 @@ export default class Entity<
|
|
|
604
605
|
return obj;
|
|
605
606
|
}
|
|
606
607
|
|
|
608
|
+
public $setNymph(nymph: Nymph) {
|
|
609
|
+
this.$nymph = nymph;
|
|
610
|
+
if (!this.$asleep()) {
|
|
611
|
+
const recurseData = (data: any) => {
|
|
612
|
+
if (Array.isArray(data)) {
|
|
613
|
+
for (let name in data) {
|
|
614
|
+
recurseData(data[name]);
|
|
615
|
+
}
|
|
616
|
+
} else if (data instanceof Entity) {
|
|
617
|
+
if (data.$nymph !== nymph) {
|
|
618
|
+
data.$setNymph(nymph);
|
|
619
|
+
}
|
|
620
|
+
} else if (typeof data === 'object') {
|
|
621
|
+
for (let name in data) {
|
|
622
|
+
recurseData(data[name]);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
recurseData(this.$data);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
607
630
|
public $getGuaranteedGUID() {
|
|
608
631
|
if (this.guid !== null) {
|
|
609
632
|
return this.guid;
|
|
@@ -648,10 +671,12 @@ export default class Entity<
|
|
|
648
671
|
return this.$clientEnabledMethods;
|
|
649
672
|
}
|
|
650
673
|
|
|
651
|
-
public async $delete(): Promise<
|
|
652
|
-
this.$
|
|
674
|
+
public async $delete(): Promise<void> {
|
|
675
|
+
await this.$wake();
|
|
653
676
|
|
|
654
|
-
|
|
677
|
+
if (!(await this.$nymph.deleteEntity(this))) {
|
|
678
|
+
throw new MethodFailedError('Failed to delete entity.');
|
|
679
|
+
}
|
|
655
680
|
}
|
|
656
681
|
|
|
657
682
|
public $equals(object: any) {
|
|
@@ -1203,16 +1228,14 @@ export default class Entity<
|
|
|
1203
1228
|
}
|
|
1204
1229
|
const refresh = await this.$nymph.getEntity(
|
|
1205
1230
|
{
|
|
1206
|
-
class: this
|
|
1207
|
-
this.constructor as EntityConstructor,
|
|
1208
|
-
),
|
|
1231
|
+
class: this.constructor as EntityConstructor,
|
|
1209
1232
|
skipCache: true,
|
|
1210
1233
|
skipAc: this.$skipAc,
|
|
1211
1234
|
},
|
|
1212
1235
|
{ type: '&', guid: this.guid },
|
|
1213
1236
|
);
|
|
1214
1237
|
if (refresh == null) {
|
|
1215
|
-
|
|
1238
|
+
throw new MethodFailedError("The entity's data could not be refreshed.");
|
|
1216
1239
|
}
|
|
1217
1240
|
this.tags = refresh.tags;
|
|
1218
1241
|
this.cdate = refresh.cdate;
|
|
@@ -1227,10 +1250,12 @@ export default class Entity<
|
|
|
1227
1250
|
this.tags = difference(this.tags, tags);
|
|
1228
1251
|
}
|
|
1229
1252
|
|
|
1230
|
-
public async $save(): Promise<
|
|
1253
|
+
public async $save(): Promise<void> {
|
|
1231
1254
|
await this.$wake();
|
|
1232
1255
|
|
|
1233
|
-
|
|
1256
|
+
if (!(await this.$nymph.saveEntity(this))) {
|
|
1257
|
+
throw new MethodFailedError('Failed to save entity.');
|
|
1258
|
+
}
|
|
1234
1259
|
}
|
|
1235
1260
|
|
|
1236
1261
|
public $toReference(existingOnly?: boolean) {
|
package/src/Entity.types.ts
CHANGED
|
@@ -69,9 +69,9 @@ export interface DataObjectInterface {
|
|
|
69
69
|
/**
|
|
70
70
|
* Delete the object from storage.
|
|
71
71
|
*
|
|
72
|
-
*
|
|
72
|
+
* An error is thrown on failure.
|
|
73
73
|
*/
|
|
74
|
-
$delete(): Promise<
|
|
74
|
+
$delete(): Promise<void>;
|
|
75
75
|
/**
|
|
76
76
|
* Perform a more strict comparison of this object to another.
|
|
77
77
|
*
|
|
@@ -100,18 +100,16 @@ export interface DataObjectInterface {
|
|
|
100
100
|
/**
|
|
101
101
|
* Refresh the object from storage. (Bypasses Nymph's cache.)
|
|
102
102
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* @returns False if the data has not been saved, 0 if it can't be refreshed, true on success.
|
|
103
|
+
* @returns False if the data has not been saved, true on success. An error is
|
|
104
|
+
* thrown on failure.
|
|
107
105
|
*/
|
|
108
|
-
$refresh(): Promise<boolean
|
|
106
|
+
$refresh(): Promise<boolean>;
|
|
109
107
|
/**
|
|
110
108
|
* Save the object to storage.
|
|
111
109
|
*
|
|
112
|
-
*
|
|
110
|
+
* An error is thrown on failure.
|
|
113
111
|
*/
|
|
114
|
-
$save(): Promise<
|
|
112
|
+
$save(): Promise<void>;
|
|
115
113
|
/**
|
|
116
114
|
* The object's data.
|
|
117
115
|
*/
|
|
@@ -172,7 +170,8 @@ export interface EntityInterface extends DataObjectInterface {
|
|
|
172
170
|
/**
|
|
173
171
|
* Get the client enabled methods.
|
|
174
172
|
*
|
|
175
|
-
* @returns The names of methods allowed to be called by the frontend with
|
|
173
|
+
* @returns The names of methods allowed to be called by the frontend with
|
|
174
|
+
* serverCall.
|
|
176
175
|
*/
|
|
177
176
|
$getClientEnabledMethods(): string[];
|
|
178
177
|
/**
|
|
@@ -181,7 +180,8 @@ export interface EntityInterface extends DataObjectInterface {
|
|
|
181
180
|
* This should only be used by Nymph to save the data into storage.
|
|
182
181
|
*
|
|
183
182
|
* @param includeSData Whether to include the serialized data as well.
|
|
184
|
-
* @param referenceOnlyExisting Whether to only turn existing entities into
|
|
183
|
+
* @param referenceOnlyExisting Whether to only turn existing entities into
|
|
184
|
+
* references.
|
|
185
185
|
* @returns The entity's data object.
|
|
186
186
|
*/
|
|
187
187
|
$getData(includeSData?: boolean, referenceOnlyExisting?: boolean): EntityData;
|
|
@@ -256,8 +256,10 @@ export interface EntityInterface extends DataObjectInterface {
|
|
|
256
256
|
* - $allowlistTags
|
|
257
257
|
* - $allowlistData
|
|
258
258
|
*
|
|
259
|
-
* @param input The input data. Please note, this will be modified
|
|
260
|
-
*
|
|
259
|
+
* @param input The input data. Please note, this will be modified
|
|
260
|
+
* (destroyed).
|
|
261
|
+
* @param allowConflict Allow to accept data that is older than the current
|
|
262
|
+
* data.
|
|
261
263
|
*/
|
|
262
264
|
$jsonAcceptData(input: EntityJson, allowConflict?: boolean): void;
|
|
263
265
|
/**
|
|
@@ -270,8 +272,10 @@ export interface EntityInterface extends DataObjectInterface {
|
|
|
270
272
|
* - $allowlistTags
|
|
271
273
|
* - $allowlistData
|
|
272
274
|
*
|
|
273
|
-
* @param patch The patch data. Please note, this will be modified
|
|
274
|
-
*
|
|
275
|
+
* @param patch The patch data. Please note, this will be modified
|
|
276
|
+
* (destroyed).
|
|
277
|
+
* @param allowConflict Allow to accept data that is older than the current
|
|
278
|
+
* data.
|
|
275
279
|
*/
|
|
276
280
|
$jsonAcceptPatch(patch: EntityPatch, allowConflict?: boolean): void;
|
|
277
281
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './transactional.js';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { nanoid } from '@nymphjs/guid';
|
|
2
|
+
|
|
3
|
+
import Entity from '../Entity.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A decorator that runs the decorated method inside a transaction.
|
|
7
|
+
*
|
|
8
|
+
* If the method resolves, the transaction is committed. If the method throws,
|
|
9
|
+
* the transaction is rolled back.
|
|
10
|
+
*
|
|
11
|
+
* Uses $setNymph to set the transactional instance of Nymph on the entity and
|
|
12
|
+
* return to the original instance after the transaction is committed or rolled
|
|
13
|
+
* back.
|
|
14
|
+
*/
|
|
15
|
+
export function transactional(
|
|
16
|
+
target: (...args: any[]) => Promise<any>,
|
|
17
|
+
context: ClassMethodDecoratorContext,
|
|
18
|
+
) {
|
|
19
|
+
const name = String(context.name);
|
|
20
|
+
return async function (this: Entity, ...args: unknown[]) {
|
|
21
|
+
const transactionName = `${name}-${nanoid()}`;
|
|
22
|
+
const nymph = this.$nymph;
|
|
23
|
+
nymph.config.debugInfo(
|
|
24
|
+
'nymph:transactional',
|
|
25
|
+
`Starting transaction ${transactionName}`,
|
|
26
|
+
);
|
|
27
|
+
const tnymph = await nymph.startTransaction(transactionName);
|
|
28
|
+
this.$setNymph(tnymph);
|
|
29
|
+
let committed = false;
|
|
30
|
+
let result: any;
|
|
31
|
+
try {
|
|
32
|
+
result = await target.apply(this, args);
|
|
33
|
+
nymph.config.debugInfo(
|
|
34
|
+
'nymph:transactional',
|
|
35
|
+
`Committing transaction ${transactionName}`,
|
|
36
|
+
);
|
|
37
|
+
committed = await tnymph.commit(transactionName);
|
|
38
|
+
} catch (e: any) {
|
|
39
|
+
try {
|
|
40
|
+
nymph.config.debugInfo(
|
|
41
|
+
'nymph:transactional',
|
|
42
|
+
`Rolling back transaction ${transactionName}, reason: ${e.message}`,
|
|
43
|
+
);
|
|
44
|
+
await tnymph.rollback(transactionName);
|
|
45
|
+
} catch (e: any) {
|
|
46
|
+
nymph.config.debugError(
|
|
47
|
+
'nymph:transactional',
|
|
48
|
+
`Rollback of transaction ${transactionName} failed, reason: ${e.message}`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
this.$setNymph(nymph);
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.$setNymph(nymph);
|
|
56
|
+
|
|
57
|
+
if (!committed) {
|
|
58
|
+
throw new Error('Transaction could not be committed.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -1598,18 +1598,30 @@ export default abstract class NymphDriver {
|
|
|
1598
1598
|
sdata,
|
|
1599
1599
|
);
|
|
1600
1600
|
}
|
|
1601
|
+
|
|
1602
|
+
if (!success) {
|
|
1603
|
+
entity.guid = originalGuid;
|
|
1604
|
+
entity.cdate = originalCdate;
|
|
1605
|
+
entity.mdate = originalMdate;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
return success;
|
|
1601
1609
|
} catch (e: any) {
|
|
1610
|
+
if (commitTransactionCallback) {
|
|
1611
|
+
try {
|
|
1612
|
+
await commitTransactionCallback(false);
|
|
1613
|
+
} catch (e: any) {
|
|
1614
|
+
this.nymph.config.debugError(
|
|
1615
|
+
'nymph',
|
|
1616
|
+
`Rollback of entity save transaction failed, reason: ${e.message}`,
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1602
1620
|
entity.guid = originalGuid;
|
|
1603
1621
|
entity.cdate = originalCdate;
|
|
1604
1622
|
entity.mdate = originalMdate;
|
|
1605
1623
|
throw e;
|
|
1606
1624
|
}
|
|
1607
|
-
if (!success) {
|
|
1608
|
-
entity.guid = originalGuid;
|
|
1609
|
-
entity.cdate = originalCdate;
|
|
1610
|
-
entity.mdate = originalMdate;
|
|
1611
|
-
}
|
|
1612
|
-
return success;
|
|
1613
1625
|
}
|
|
1614
1626
|
|
|
1615
1627
|
/**
|