@quadnix/octo 0.0.2 → 0.0.4
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/package.json +6 -1
- package/.eslintrc.cjs +0 -26
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/src/app.type.ts +0 -56
- package/src/decorators/action.decorator.spec.ts +0 -43
- package/src/decorators/action.decorator.ts +0 -24
- package/src/decorators/anchor.decorator.ts +0 -10
- package/src/decorators/container.spec.ts +0 -104
- package/src/decorators/container.ts +0 -133
- package/src/decorators/factory.decorator.ts +0 -13
- package/src/decorators/model.decorator.ts +0 -10
- package/src/decorators/module.decorator.ts +0 -13
- package/src/decorators/overlay.decorator.ts +0 -10
- package/src/decorators/resource.decorator.ts +0 -10
- package/src/decorators/test-container.spec.ts +0 -50
- package/src/decorators/test-container.ts +0 -56
- package/src/functions/dependency/dependency.model.spec.ts +0 -43
- package/src/functions/dependency/dependency.model.ts +0 -178
- package/src/functions/diff/diff-metadata.model.ts +0 -51
- package/src/functions/diff/diff.model.ts +0 -33
- package/src/functions/diff/diff.utility.ts +0 -163
- package/src/functions/module/module.abstract.ts +0 -59
- package/src/functions/module/module.interface.ts +0 -5
- package/src/index.ts +0 -62
- package/src/models/action.interface.ts +0 -35
- package/src/models/app/app.interface.ts +0 -5
- package/src/models/app/app.model.ts +0 -111
- package/src/models/deployment/deployment.interface.ts +0 -5
- package/src/models/deployment/deployment.model.ts +0 -35
- package/src/models/environment/environment.interface.ts +0 -6
- package/src/models/environment/environment.model.spec.ts +0 -125
- package/src/models/environment/environment.model.ts +0 -55
- package/src/models/execution/execution.interface.ts +0 -8
- package/src/models/execution/execution.model.spec.ts +0 -62
- package/src/models/execution/execution.model.ts +0 -89
- package/src/models/image/image.interface.ts +0 -8
- package/src/models/image/image.model.ts +0 -62
- package/src/models/model.abstract.ts +0 -324
- package/src/models/model.interface.ts +0 -42
- package/src/models/pipeline/pipeline.interface.ts +0 -6
- package/src/models/pipeline/pipeline.model.ts +0 -47
- package/src/models/region/region.interface.ts +0 -5
- package/src/models/region/region.model.spec.ts +0 -162
- package/src/models/region/region.model.ts +0 -48
- package/src/models/server/server.interface.ts +0 -7
- package/src/models/server/server.model.ts +0 -56
- package/src/models/service/service.interface.ts +0 -5
- package/src/models/service/service.model.ts +0 -25
- package/src/models/support/support.interface.ts +0 -6
- package/src/models/support/support.model.spec.ts +0 -78
- package/src/models/support/support.model.ts +0 -52
- package/src/overlays/anchor.abstract.ts +0 -41
- package/src/overlays/anchor.interface.ts +0 -7
- package/src/overlays/anchor.spec.ts +0 -41
- package/src/overlays/overlay-data.repository.ts +0 -76
- package/src/overlays/overlay.abstract.ts +0 -64
- package/src/overlays/overlay.interface.ts +0 -9
- package/src/overlays/overlay.service.ts +0 -38
- package/src/overlays/overlay.spec.ts +0 -86
- package/src/resources/resource-action.interface.ts +0 -9
- package/src/resources/resource-data.repository.spec.ts +0 -133
- package/src/resources/resource-data.repository.ts +0 -92
- package/src/resources/resource.abstract.ts +0 -108
- package/src/resources/resource.interface.ts +0 -7
- package/src/resources/resource.spec.ts +0 -60
- package/src/resources/shared-resource.abstract.ts +0 -53
- package/src/resources/shared-resource.spec.ts +0 -133
- package/src/services/serialization/model/__snapshots__/model-serialization.service.spec.ts.snap +0 -656
- package/src/services/serialization/model/model-serialization.service.spec.ts +0 -195
- package/src/services/serialization/model/model-serialization.service.ts +0 -172
- package/src/services/serialization/resource/__snapshots__/resource-serialization.service.spec.ts.snap +0 -284
- package/src/services/serialization/resource/resource-serialization.service.spec.ts +0 -311
- package/src/services/serialization/resource/resource-serialization.service.ts +0 -166
- package/src/services/state-management/local.state-provider.spec.ts +0 -42
- package/src/services/state-management/local.state-provider.ts +0 -23
- package/src/services/state-management/state-management.service.ts +0 -43
- package/src/services/state-management/state-provider.interface.ts +0 -5
- package/src/services/transaction/__snapshots__/transaction.service.spec.ts.snap +0 -284
- package/src/services/transaction/transaction.service.spec.ts +0 -823
- package/src/services/transaction/transaction.service.ts +0 -386
- package/test/app.e2e-spec.ts +0 -104
- package/test/helpers/test-classes.ts +0 -31
- package/test/models/__snapshots__/model.e2e-spec.ts.snap +0 -451
- package/test/models/model.e2e-spec.ts +0 -366
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -9
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import { UnknownResource } from '../../../app.type.js';
|
|
2
|
-
import { Container } from '../../../decorators/container.js';
|
|
3
|
-
import { ResourceDataRepository, ResourceDataRepositoryFactory } from '../../../resources/resource-data.repository.js';
|
|
4
|
-
import { AResource } from '../../../resources/resource.abstract.js';
|
|
5
|
-
import { ASharedResource } from '../../../resources/shared-resource.abstract.js';
|
|
6
|
-
import { ResourceSerializationService } from './resource-serialization.service.js';
|
|
7
|
-
|
|
8
|
-
class TestResource extends AResource<TestResource> {
|
|
9
|
-
readonly MODEL_NAME: string = 'test-resource';
|
|
10
|
-
|
|
11
|
-
constructor(resourceId: string, properties: { [k: string]: string } = {}, parents: [TestResource?] = []) {
|
|
12
|
-
super(resourceId, properties, parents as AResource<TestResource>[]);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
class SharedTestResource extends ASharedResource<TestResource> {
|
|
17
|
-
readonly MODEL_NAME: string = 'shared-test-resource';
|
|
18
|
-
|
|
19
|
-
constructor(resourceId: string, properties: object, parents: [TestResource?]) {
|
|
20
|
-
super(resourceId, {}, parents as AResource<TestResource>[]);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('Resource Serialization Service UT', () => {
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
Container.registerFactory(ResourceDataRepository, ResourceDataRepositoryFactory);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
afterEach(() => {
|
|
30
|
-
Container.reset();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('deserialize()', () => {
|
|
34
|
-
it('should throw error when de-serializing an unknown class', async () => {
|
|
35
|
-
const resource1 = new TestResource('resource-1');
|
|
36
|
-
resource1.properties['key1'] = 'value1';
|
|
37
|
-
resource1.response['response1'] = 'value1';
|
|
38
|
-
|
|
39
|
-
const resources = [resource1];
|
|
40
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
41
|
-
|
|
42
|
-
const service = new ResourceSerializationService();
|
|
43
|
-
|
|
44
|
-
await expect(async () => {
|
|
45
|
-
const serializedOutput = await service.serialize();
|
|
46
|
-
await service.deserialize(serializedOutput);
|
|
47
|
-
}).rejects.toThrowErrorMatchingInlineSnapshot(`"Cannot read properties of undefined (reading 'unSynth')"`);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should deserialize a single resource', async () => {
|
|
51
|
-
const resource1 = new TestResource('resource-1');
|
|
52
|
-
resource1.properties['key1'] = 'value1';
|
|
53
|
-
resource1.response['response1'] = 'value1';
|
|
54
|
-
|
|
55
|
-
const resources = [resource1];
|
|
56
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
57
|
-
|
|
58
|
-
const service = new ResourceSerializationService();
|
|
59
|
-
service.registerClass('TestResource', TestResource);
|
|
60
|
-
|
|
61
|
-
const serializedOutput = await service.serialize();
|
|
62
|
-
await service.deserialize(serializedOutput);
|
|
63
|
-
|
|
64
|
-
const resourceDataRepository = await Container.get(ResourceDataRepository);
|
|
65
|
-
const deserializedResources = resourceDataRepository.getByProperties();
|
|
66
|
-
expect(deserializedResources).toMatchSnapshot();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should deserialize a resource with complex properties', async () => {
|
|
70
|
-
const resource1 = new TestResource('resource-1');
|
|
71
|
-
resource1.properties['key1'] = { key2: { key3: 'value3' }, key4: 'value4' };
|
|
72
|
-
resource1.response['response1'] = 'value1';
|
|
73
|
-
|
|
74
|
-
const resources = [resource1];
|
|
75
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
76
|
-
|
|
77
|
-
const service = new ResourceSerializationService();
|
|
78
|
-
service.registerClass('TestResource', TestResource);
|
|
79
|
-
|
|
80
|
-
const serializedOutput = await service.serialize();
|
|
81
|
-
await service.deserialize(serializedOutput);
|
|
82
|
-
|
|
83
|
-
const resourceDataRepository = await Container.get(ResourceDataRepository);
|
|
84
|
-
const deserializedResources = resourceDataRepository.getByProperties();
|
|
85
|
-
expect(deserializedResources).toMatchSnapshot();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should deserialize a single shared resource', async () => {
|
|
89
|
-
const resource1 = new TestResource('resource-1');
|
|
90
|
-
resource1.properties['key1'] = 'value1';
|
|
91
|
-
resource1.response['response1'] = 'value1';
|
|
92
|
-
const resource2 = new SharedTestResource('resource-2', {}, [resource1]);
|
|
93
|
-
|
|
94
|
-
const resources = [resource1, resource2];
|
|
95
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
96
|
-
|
|
97
|
-
const service = new ResourceSerializationService();
|
|
98
|
-
service.registerClass('TestResource', TestResource);
|
|
99
|
-
service.registerClass('SharedTestResource', SharedTestResource);
|
|
100
|
-
|
|
101
|
-
const serializedOutput = await service.serialize();
|
|
102
|
-
await service.deserialize(serializedOutput);
|
|
103
|
-
|
|
104
|
-
const resourceDataRepository = await Container.get(ResourceDataRepository);
|
|
105
|
-
const deserializedResources = resourceDataRepository.getByProperties();
|
|
106
|
-
expect(deserializedResources).toMatchSnapshot();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should throw error trying to deserialize a shared resource without corresponding resource', async () => {
|
|
110
|
-
const resource1 = new TestResource('resource-1');
|
|
111
|
-
resource1.properties['key1'] = 'value1';
|
|
112
|
-
resource1.response['response1'] = 'value1';
|
|
113
|
-
const resource2 = new SharedTestResource('resource-2', {}, [resource1]);
|
|
114
|
-
|
|
115
|
-
const resources = [resource2];
|
|
116
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
117
|
-
|
|
118
|
-
const service = new ResourceSerializationService();
|
|
119
|
-
service.registerClass('TestResource', TestResource);
|
|
120
|
-
service.registerClass('SharedTestResource', SharedTestResource);
|
|
121
|
-
service.setResourceDeserializationTimeout(50);
|
|
122
|
-
|
|
123
|
-
const serializedOutput = await service.serialize();
|
|
124
|
-
|
|
125
|
-
await expect(async () => {
|
|
126
|
-
await service.deserialize(serializedOutput);
|
|
127
|
-
}).rejects.toMatchInlineSnapshot(`[Error: DeReferencing resource operation timed out!]`);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should deserialize dependencies', async () => {
|
|
131
|
-
const resource1 = new TestResource('resource-1');
|
|
132
|
-
resource1.properties['key1'] = 'value1';
|
|
133
|
-
resource1.response['response1'] = 'value1';
|
|
134
|
-
const resource2 = new TestResource('resource-2');
|
|
135
|
-
resource2.properties['key2'] = 'value2';
|
|
136
|
-
resource2.response['response2'] = 'value2';
|
|
137
|
-
resource1.addChild('resourceId', resource2, 'resourceId');
|
|
138
|
-
|
|
139
|
-
const resources = [resource1, resource2];
|
|
140
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
141
|
-
|
|
142
|
-
const service = new ResourceSerializationService();
|
|
143
|
-
service.registerClass('TestResource', TestResource);
|
|
144
|
-
|
|
145
|
-
const serializedOutput = await service.serialize();
|
|
146
|
-
await service.deserialize(serializedOutput);
|
|
147
|
-
|
|
148
|
-
const resourceDataRepository = await Container.get(ResourceDataRepository);
|
|
149
|
-
const deserializedResources = resourceDataRepository.getByProperties();
|
|
150
|
-
|
|
151
|
-
const resource1Deserialized = deserializedResources.find((r) => r.resourceId === 'resource-1');
|
|
152
|
-
const resource2Deserialized = resource1Deserialized!.getChildren()['test-resource'][0]
|
|
153
|
-
.to as AResource<TestResource>;
|
|
154
|
-
expect(resource2Deserialized.resourceId).toBe('resource-2');
|
|
155
|
-
expect((resource2Deserialized.getParents()['test-resource'][0].to as AResource<TestResource>).resourceId).toBe(
|
|
156
|
-
'resource-1',
|
|
157
|
-
);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should deserialize dependencies in reverse order', async () => {
|
|
161
|
-
const resource1 = new TestResource('resource-1');
|
|
162
|
-
resource1.properties['key1'] = 'value1';
|
|
163
|
-
resource1.response['response1'] = 'value1';
|
|
164
|
-
const resource2 = new TestResource('resource-2');
|
|
165
|
-
resource2.properties['key2'] = 'value2';
|
|
166
|
-
resource2.response['response2'] = 'value2';
|
|
167
|
-
resource1.addChild('resourceId', resource2, 'resourceId');
|
|
168
|
-
|
|
169
|
-
const resources = [resource2, resource1];
|
|
170
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
171
|
-
|
|
172
|
-
const service = new ResourceSerializationService();
|
|
173
|
-
service.registerClass('TestResource', TestResource);
|
|
174
|
-
|
|
175
|
-
const serializedOutput = await service.serialize();
|
|
176
|
-
await service.deserialize(serializedOutput);
|
|
177
|
-
|
|
178
|
-
const resourceDataRepository = await Container.get(ResourceDataRepository);
|
|
179
|
-
const deserializedResources = resourceDataRepository.getByProperties();
|
|
180
|
-
|
|
181
|
-
const resource1Deserialized = deserializedResources.find((r) => r.resourceId === 'resource-1');
|
|
182
|
-
const resource2Deserialized = resource1Deserialized!.getChildren()['test-resource'][0]
|
|
183
|
-
.to as AResource<TestResource>;
|
|
184
|
-
expect(resource2Deserialized.resourceId).toBe('resource-2');
|
|
185
|
-
expect((resource2Deserialized.getParents()['test-resource'][0].to as AResource<TestResource>).resourceId).toBe(
|
|
186
|
-
'resource-1',
|
|
187
|
-
);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should have exact same dependencies on deserialized object as original object', async () => {
|
|
191
|
-
const resource1 = new TestResource('resource-1');
|
|
192
|
-
resource1.properties['key1'] = 'value1';
|
|
193
|
-
resource1.response['response1'] = 'value1';
|
|
194
|
-
const resource2 = new TestResource('resource-2');
|
|
195
|
-
resource2.properties['key2'] = 'value2';
|
|
196
|
-
resource2.response['response2'] = 'value2';
|
|
197
|
-
resource1.addChild('resourceId', resource2, 'resourceId');
|
|
198
|
-
const resource3 = new SharedTestResource('resource-3', {}, [resource1]);
|
|
199
|
-
|
|
200
|
-
const resources = [resource1, resource2, resource3];
|
|
201
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
202
|
-
|
|
203
|
-
const service = new ResourceSerializationService();
|
|
204
|
-
service.registerClass('TestResource', TestResource);
|
|
205
|
-
service.registerClass('SharedTestResource', SharedTestResource);
|
|
206
|
-
|
|
207
|
-
const serializedOutput1 = await service.serialize();
|
|
208
|
-
const oldResourcesDependencies = serializedOutput1.dependencies.sort((a, b) =>
|
|
209
|
-
a.from + a.to > b.from + b.to ? 1 : b.from + b.to > a.from + a.to ? -1 : 0,
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
await service.deserialize(serializedOutput1);
|
|
213
|
-
const resourceDataRepository = await Container.get(ResourceDataRepository);
|
|
214
|
-
const deserializedResources = resourceDataRepository.getByProperties();
|
|
215
|
-
|
|
216
|
-
// An easy way to get dependencies from `deserializedResources` is to serialize it again.
|
|
217
|
-
await Container.get(ResourceDataRepository, {
|
|
218
|
-
args: [true, [...deserializedResources], [...deserializedResources]],
|
|
219
|
-
});
|
|
220
|
-
const serializedOutput2 = await service.serialize();
|
|
221
|
-
const newResourcesDependencies = serializedOutput2.dependencies.sort((a, b) =>
|
|
222
|
-
a.from + a.to > b.from + b.to ? 1 : b.from + b.to > a.from + a.to ? -1 : 0,
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
expect(newResourcesDependencies).toEqual(oldResourcesDependencies);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('should initialize ResourceDataRepository with separate old and new resources', async () => {
|
|
229
|
-
const resource1 = new TestResource('resource-1');
|
|
230
|
-
resource1.properties['key1'] = 'value1';
|
|
231
|
-
resource1.response['response1'] = 'value1';
|
|
232
|
-
|
|
233
|
-
const resources = [resource1];
|
|
234
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
235
|
-
|
|
236
|
-
const service = new ResourceSerializationService();
|
|
237
|
-
service.registerClass('TestResource', TestResource);
|
|
238
|
-
|
|
239
|
-
const serializedOutput = await service.serialize();
|
|
240
|
-
await service.deserialize(serializedOutput);
|
|
241
|
-
|
|
242
|
-
const resourceDataRepository = await Container.get(ResourceDataRepository);
|
|
243
|
-
|
|
244
|
-
// Manipulate new resources. This must not affect old resources in any way.
|
|
245
|
-
resourceDataRepository.getById('resource-1')!.properties['key1'] = 'value2';
|
|
246
|
-
expect(resourceDataRepository['newResources'][0]).not.toEqual(resourceDataRepository['oldResources'][0]);
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
describe('serialize()', () => {
|
|
251
|
-
it('should serialize an empty array', async () => {
|
|
252
|
-
await Container.get(ResourceDataRepository, { args: [true, [], []] });
|
|
253
|
-
|
|
254
|
-
const service = new ResourceSerializationService();
|
|
255
|
-
expect(await service.serialize()).toMatchSnapshot();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it('should serialize non-empty array', async () => {
|
|
259
|
-
const resources: UnknownResource[] = [new TestResource('resource-1')];
|
|
260
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
261
|
-
|
|
262
|
-
const service = new ResourceSerializationService();
|
|
263
|
-
expect(await service.serialize()).toMatchSnapshot();
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it('should not serialize resources marked for deletion', async () => {
|
|
267
|
-
const resource1 = new TestResource('resource-1');
|
|
268
|
-
resource1.properties['key1'] = 'value1';
|
|
269
|
-
resource1.response['response1'] = 'value1';
|
|
270
|
-
const resource2 = new TestResource('resource-2');
|
|
271
|
-
resource2.properties['key2'] = 'value2';
|
|
272
|
-
resource2.response['response2'] = 'value2';
|
|
273
|
-
resource2.markDeleted();
|
|
274
|
-
|
|
275
|
-
const resources = [resource1, resource2];
|
|
276
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
277
|
-
|
|
278
|
-
const service = new ResourceSerializationService();
|
|
279
|
-
expect(await service.serialize()).toMatchSnapshot();
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it('should serialize dependencies and properties and resources', async () => {
|
|
283
|
-
const resource1 = new TestResource('resource-1');
|
|
284
|
-
resource1.properties['key1'] = 'value1';
|
|
285
|
-
resource1.response['response1'] = 'value1';
|
|
286
|
-
const resource2 = new TestResource('resource-2');
|
|
287
|
-
resource2.properties['key2'] = 'value2';
|
|
288
|
-
resource2.response['response2'] = 'value2';
|
|
289
|
-
resource1.addChild('resourceId', resource2, 'resourceId');
|
|
290
|
-
|
|
291
|
-
const resources = [resource1, resource2];
|
|
292
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
293
|
-
|
|
294
|
-
const service = new ResourceSerializationService();
|
|
295
|
-
expect(await service.serialize()).toMatchSnapshot();
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('should serialize shared resources', async () => {
|
|
299
|
-
const resource1 = new TestResource('resource-1');
|
|
300
|
-
const resource2 = new SharedTestResource('resource-2', {}, [resource1]);
|
|
301
|
-
resource2.properties['key2'] = 'value2';
|
|
302
|
-
resource2.response['response2'] = 'value2';
|
|
303
|
-
|
|
304
|
-
const resources = [resource1, resource2];
|
|
305
|
-
await Container.get(ResourceDataRepository, { args: [true, [...resources], [...resources]] });
|
|
306
|
-
|
|
307
|
-
const service = new ResourceSerializationService();
|
|
308
|
-
expect(await service.serialize()).toMatchSnapshot();
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
});
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { ActionOutputs, ResourceSerializedOutput, UnknownResource } from '../../../app.type.js';
|
|
2
|
-
import { Container } from '../../../decorators/container.js';
|
|
3
|
-
import { Factory } from '../../../decorators/factory.decorator.js';
|
|
4
|
-
import { IDependency } from '../../../functions/dependency/dependency.model.js';
|
|
5
|
-
import { ResourceDataRepository } from '../../../resources/resource-data.repository.js';
|
|
6
|
-
|
|
7
|
-
export class ResourceSerializationService {
|
|
8
|
-
private RESOURCE_DESERIALIZATION_TIMEOUT_IN_MS = 5000;
|
|
9
|
-
|
|
10
|
-
private readonly classMapping: { [key: string]: any } = {};
|
|
11
|
-
|
|
12
|
-
private async _deserialize(serializedOutput: ResourceSerializedOutput): Promise<ActionOutputs> {
|
|
13
|
-
const deReferencePromises: {
|
|
14
|
-
[p: string]: [Promise<boolean>, (value: boolean) => void, (error: Error) => void, NodeJS.Timeout];
|
|
15
|
-
} = {};
|
|
16
|
-
const parents: { [p: string]: string[] } = {};
|
|
17
|
-
const seen: ActionOutputs = {};
|
|
18
|
-
|
|
19
|
-
const deReferenceResource = async (resourceId: string): Promise<UnknownResource> => {
|
|
20
|
-
if (!seen[resourceId]) {
|
|
21
|
-
if (deReferencePromises[resourceId]) {
|
|
22
|
-
await deReferencePromises[resourceId][0];
|
|
23
|
-
} else {
|
|
24
|
-
deReferencePromises[resourceId] = [] as any;
|
|
25
|
-
const promise = new Promise<boolean>((resolve, reject) => {
|
|
26
|
-
deReferencePromises[resourceId][1] = resolve;
|
|
27
|
-
deReferencePromises[resourceId][2] = reject;
|
|
28
|
-
});
|
|
29
|
-
deReferencePromises[resourceId][0] = promise;
|
|
30
|
-
|
|
31
|
-
deReferencePromises[resourceId][3] = setTimeout(() => {
|
|
32
|
-
deReferencePromises[resourceId][2](new Error('DeReferencing resource operation timed out!'));
|
|
33
|
-
}, this.RESOURCE_DESERIALIZATION_TIMEOUT_IN_MS);
|
|
34
|
-
await promise;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return seen[resourceId];
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const deserializeResource = async (
|
|
42
|
-
resourceId: string,
|
|
43
|
-
parents: string[],
|
|
44
|
-
isSharedResource: boolean,
|
|
45
|
-
): Promise<UnknownResource> => {
|
|
46
|
-
const { className, resource } = isSharedResource
|
|
47
|
-
? serializedOutput.sharedResources[resourceId]
|
|
48
|
-
: serializedOutput.resources[resourceId];
|
|
49
|
-
const deserializationClass = this.classMapping[className];
|
|
50
|
-
|
|
51
|
-
const deserializedResource = await deserializationClass.unSynth(
|
|
52
|
-
deserializationClass,
|
|
53
|
-
resource,
|
|
54
|
-
parents || [],
|
|
55
|
-
deReferenceResource,
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
seen[resourceId] = deserializedResource;
|
|
59
|
-
if (deReferencePromises[resourceId]) {
|
|
60
|
-
deReferencePromises[resourceId][1](true);
|
|
61
|
-
clearTimeout(deReferencePromises[resourceId][3]);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return deserializedResource;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Re-generate resource parents from dependencies.
|
|
68
|
-
for (const d of serializedOutput.dependencies) {
|
|
69
|
-
const fromResourceId = d.from.split('=')[1];
|
|
70
|
-
const toResourceId = d.to.split('=')[1];
|
|
71
|
-
|
|
72
|
-
if (!parents[fromResourceId]) {
|
|
73
|
-
parents[fromResourceId] = [];
|
|
74
|
-
}
|
|
75
|
-
if (!parents[toResourceId]) {
|
|
76
|
-
parents[toResourceId] = [];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Resources don't have other relationships than parent-child relations.
|
|
80
|
-
if (d.relationship?.type === 'child' && !parents[fromResourceId].includes(toResourceId)) {
|
|
81
|
-
parents[fromResourceId].push(toResourceId);
|
|
82
|
-
} else if (d.relationship?.type === 'parent' && !parents[toResourceId].includes(fromResourceId)) {
|
|
83
|
-
parents[toResourceId].push(fromResourceId);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Deserialize all serialized resources.
|
|
88
|
-
const promiseToDeserializeResources: Promise<UnknownResource>[] = [];
|
|
89
|
-
for (const resourceId in serializedOutput.resources) {
|
|
90
|
-
promiseToDeserializeResources.push(deserializeResource(resourceId, parents[resourceId], false));
|
|
91
|
-
}
|
|
92
|
-
await Promise.all(promiseToDeserializeResources);
|
|
93
|
-
|
|
94
|
-
// Deserialize all serialized shared-resources.
|
|
95
|
-
const promiseToDeserializeSharedResources: Promise<UnknownResource>[] = [];
|
|
96
|
-
for (const resourceId in serializedOutput.sharedResources) {
|
|
97
|
-
promiseToDeserializeSharedResources.push(deserializeResource(resourceId, parents[resourceId], true));
|
|
98
|
-
}
|
|
99
|
-
await Promise.all(promiseToDeserializeSharedResources);
|
|
100
|
-
|
|
101
|
-
return seen;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async deserialize(serializedOutput: ResourceSerializedOutput): Promise<void> {
|
|
105
|
-
const deserializedOutput = await this._deserialize(serializedOutput);
|
|
106
|
-
const deserializedOutputCopy = await this._deserialize(serializedOutput);
|
|
107
|
-
|
|
108
|
-
// Initialize a new instance of ResourceDataRepository, overwriting the previous one.
|
|
109
|
-
await Container.get(ResourceDataRepository, {
|
|
110
|
-
args: [true, Object.values(deserializedOutput), Object.values(deserializedOutputCopy)],
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
registerClass(className: string, deserializationClass: any): void {
|
|
115
|
-
this.classMapping[className] = deserializationClass;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async serialize(): Promise<ResourceSerializedOutput> {
|
|
119
|
-
const resourceDataRepository = await Container.get(ResourceDataRepository);
|
|
120
|
-
const resources = resourceDataRepository.getByProperties();
|
|
121
|
-
|
|
122
|
-
const dependencies: IDependency[] = [];
|
|
123
|
-
const serializedResources: ResourceSerializedOutput['resources'] = {};
|
|
124
|
-
const sharedSerializedResources: ResourceSerializedOutput['sharedResources'] = {};
|
|
125
|
-
|
|
126
|
-
for (const resource of resources) {
|
|
127
|
-
// Skip serializing resources marked as deleted.
|
|
128
|
-
if (resource.isMarkedDeleted()) {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const resourceDependencies = resource['dependencies'].map((d) => d.synth());
|
|
133
|
-
dependencies.push(...resourceDependencies);
|
|
134
|
-
|
|
135
|
-
if (resource.MODEL_TYPE === 'shared-resource') {
|
|
136
|
-
sharedSerializedResources[resource.resourceId] = {
|
|
137
|
-
className: resource.constructor.name,
|
|
138
|
-
resource: resource.synth(),
|
|
139
|
-
};
|
|
140
|
-
} else {
|
|
141
|
-
serializedResources[resource.resourceId] = {
|
|
142
|
-
className: resource.constructor.name,
|
|
143
|
-
resource: resource.synth(),
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return { dependencies, resources: serializedResources, sharedResources: sharedSerializedResources };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
setResourceDeserializationTimeout(timeoutInMs: number): void {
|
|
152
|
-
this.RESOURCE_DESERIALIZATION_TIMEOUT_IN_MS = timeoutInMs;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
@Factory<ResourceSerializationService>(ResourceSerializationService)
|
|
157
|
-
export class ResourceSerializationServiceFactory {
|
|
158
|
-
private static instance: ResourceSerializationService;
|
|
159
|
-
|
|
160
|
-
static async create(): Promise<ResourceSerializationService> {
|
|
161
|
-
if (!this.instance) {
|
|
162
|
-
this.instance = new ResourceSerializationService();
|
|
163
|
-
}
|
|
164
|
-
return this.instance;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { unlink } from 'fs';
|
|
2
|
-
import { dirname, join } from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import { promisify } from 'util';
|
|
5
|
-
import { LocalStateProvider } from './local.state-provider.js';
|
|
6
|
-
import { StateManagementService } from './state-management.service.js';
|
|
7
|
-
|
|
8
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const unlinkAsync = promisify(unlink);
|
|
10
|
-
|
|
11
|
-
describe('LocalStateProvider UT', () => {
|
|
12
|
-
let filePath;
|
|
13
|
-
|
|
14
|
-
afterEach(async () => {
|
|
15
|
-
if (filePath) {
|
|
16
|
-
await unlinkAsync(filePath);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should be able to save state', async () => {
|
|
21
|
-
filePath = join(__dirname, 'manifest.json');
|
|
22
|
-
|
|
23
|
-
const localStateProvider = new LocalStateProvider(__dirname);
|
|
24
|
-
const stateManagementService = new StateManagementService(localStateProvider);
|
|
25
|
-
|
|
26
|
-
const data = Buffer.from(
|
|
27
|
-
JSON.stringify({
|
|
28
|
-
key1: 'value1',
|
|
29
|
-
key2: 'value2',
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
32
|
-
await stateManagementService.saveState('manifest.json', data);
|
|
33
|
-
|
|
34
|
-
const manifestData = await stateManagementService.getState('manifest.json');
|
|
35
|
-
expect(JSON.parse(manifestData.toString())).toMatchInlineSnapshot(`
|
|
36
|
-
{
|
|
37
|
-
"key1": "value1",
|
|
38
|
-
"key2": "value2",
|
|
39
|
-
}
|
|
40
|
-
`);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile } from 'fs';
|
|
2
|
-
import { join, resolve } from 'path';
|
|
3
|
-
import { promisify } from 'util';
|
|
4
|
-
import { IStateProvider } from './state-provider.interface.js';
|
|
5
|
-
|
|
6
|
-
const readFileAsync = promisify(readFile);
|
|
7
|
-
const writeFileAsync = promisify(writeFile);
|
|
8
|
-
|
|
9
|
-
export class LocalStateProvider implements IStateProvider {
|
|
10
|
-
private readonly localStateDirectoryPath: string;
|
|
11
|
-
|
|
12
|
-
constructor(localStateDirectoryPath: string) {
|
|
13
|
-
this.localStateDirectoryPath = resolve(localStateDirectoryPath);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async getState(stateFileName: string): Promise<Buffer> {
|
|
17
|
-
return readFileAsync(join(this.localStateDirectoryPath, stateFileName));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async saveState(stateFileName: string, data: Buffer): Promise<void> {
|
|
21
|
-
await writeFileAsync(join(this.localStateDirectoryPath, stateFileName), data);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Factory } from '../../decorators/factory.decorator.js';
|
|
2
|
-
import { IStateProvider } from './state-provider.interface.js';
|
|
3
|
-
|
|
4
|
-
export class StateManagementService {
|
|
5
|
-
private readonly stateProvider: IStateProvider;
|
|
6
|
-
|
|
7
|
-
constructor(stateProvider: IStateProvider) {
|
|
8
|
-
this.stateProvider = stateProvider;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async getState(stateFileName: string, defaultValue?: any): Promise<Buffer> {
|
|
12
|
-
try {
|
|
13
|
-
return await this.stateProvider.getState(stateFileName);
|
|
14
|
-
} catch (error) {
|
|
15
|
-
if (error.code === 'ENOENT' && defaultValue) {
|
|
16
|
-
return defaultValue;
|
|
17
|
-
} else {
|
|
18
|
-
throw error;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async saveState(stateFileName: string, data: Buffer): Promise<void> {
|
|
24
|
-
return this.stateProvider.saveState(stateFileName, data);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
@Factory<StateManagementService>(StateManagementService)
|
|
29
|
-
export class StateManagementServiceFactory {
|
|
30
|
-
private static instance: StateManagementService;
|
|
31
|
-
|
|
32
|
-
static async create(stateProvider: IStateProvider): Promise<StateManagementService> {
|
|
33
|
-
if (this.instance) {
|
|
34
|
-
return this.instance;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!stateProvider) {
|
|
38
|
-
throw new Error(`Failed to create instance of ${StateManagementService.name} due to insufficient arguments!`);
|
|
39
|
-
}
|
|
40
|
-
this.instance = new StateManagementService(stateProvider);
|
|
41
|
-
return this.instance;
|
|
42
|
-
}
|
|
43
|
-
}
|