@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.
Files changed (86) hide show
  1. package/package.json +6 -1
  2. package/.eslintrc.cjs +0 -26
  3. package/dist/tsconfig.build.tsbuildinfo +0 -1
  4. package/src/app.type.ts +0 -56
  5. package/src/decorators/action.decorator.spec.ts +0 -43
  6. package/src/decorators/action.decorator.ts +0 -24
  7. package/src/decorators/anchor.decorator.ts +0 -10
  8. package/src/decorators/container.spec.ts +0 -104
  9. package/src/decorators/container.ts +0 -133
  10. package/src/decorators/factory.decorator.ts +0 -13
  11. package/src/decorators/model.decorator.ts +0 -10
  12. package/src/decorators/module.decorator.ts +0 -13
  13. package/src/decorators/overlay.decorator.ts +0 -10
  14. package/src/decorators/resource.decorator.ts +0 -10
  15. package/src/decorators/test-container.spec.ts +0 -50
  16. package/src/decorators/test-container.ts +0 -56
  17. package/src/functions/dependency/dependency.model.spec.ts +0 -43
  18. package/src/functions/dependency/dependency.model.ts +0 -178
  19. package/src/functions/diff/diff-metadata.model.ts +0 -51
  20. package/src/functions/diff/diff.model.ts +0 -33
  21. package/src/functions/diff/diff.utility.ts +0 -163
  22. package/src/functions/module/module.abstract.ts +0 -59
  23. package/src/functions/module/module.interface.ts +0 -5
  24. package/src/index.ts +0 -62
  25. package/src/models/action.interface.ts +0 -35
  26. package/src/models/app/app.interface.ts +0 -5
  27. package/src/models/app/app.model.ts +0 -111
  28. package/src/models/deployment/deployment.interface.ts +0 -5
  29. package/src/models/deployment/deployment.model.ts +0 -35
  30. package/src/models/environment/environment.interface.ts +0 -6
  31. package/src/models/environment/environment.model.spec.ts +0 -125
  32. package/src/models/environment/environment.model.ts +0 -55
  33. package/src/models/execution/execution.interface.ts +0 -8
  34. package/src/models/execution/execution.model.spec.ts +0 -62
  35. package/src/models/execution/execution.model.ts +0 -89
  36. package/src/models/image/image.interface.ts +0 -8
  37. package/src/models/image/image.model.ts +0 -62
  38. package/src/models/model.abstract.ts +0 -324
  39. package/src/models/model.interface.ts +0 -42
  40. package/src/models/pipeline/pipeline.interface.ts +0 -6
  41. package/src/models/pipeline/pipeline.model.ts +0 -47
  42. package/src/models/region/region.interface.ts +0 -5
  43. package/src/models/region/region.model.spec.ts +0 -162
  44. package/src/models/region/region.model.ts +0 -48
  45. package/src/models/server/server.interface.ts +0 -7
  46. package/src/models/server/server.model.ts +0 -56
  47. package/src/models/service/service.interface.ts +0 -5
  48. package/src/models/service/service.model.ts +0 -25
  49. package/src/models/support/support.interface.ts +0 -6
  50. package/src/models/support/support.model.spec.ts +0 -78
  51. package/src/models/support/support.model.ts +0 -52
  52. package/src/overlays/anchor.abstract.ts +0 -41
  53. package/src/overlays/anchor.interface.ts +0 -7
  54. package/src/overlays/anchor.spec.ts +0 -41
  55. package/src/overlays/overlay-data.repository.ts +0 -76
  56. package/src/overlays/overlay.abstract.ts +0 -64
  57. package/src/overlays/overlay.interface.ts +0 -9
  58. package/src/overlays/overlay.service.ts +0 -38
  59. package/src/overlays/overlay.spec.ts +0 -86
  60. package/src/resources/resource-action.interface.ts +0 -9
  61. package/src/resources/resource-data.repository.spec.ts +0 -133
  62. package/src/resources/resource-data.repository.ts +0 -92
  63. package/src/resources/resource.abstract.ts +0 -108
  64. package/src/resources/resource.interface.ts +0 -7
  65. package/src/resources/resource.spec.ts +0 -60
  66. package/src/resources/shared-resource.abstract.ts +0 -53
  67. package/src/resources/shared-resource.spec.ts +0 -133
  68. package/src/services/serialization/model/__snapshots__/model-serialization.service.spec.ts.snap +0 -656
  69. package/src/services/serialization/model/model-serialization.service.spec.ts +0 -195
  70. package/src/services/serialization/model/model-serialization.service.ts +0 -172
  71. package/src/services/serialization/resource/__snapshots__/resource-serialization.service.spec.ts.snap +0 -284
  72. package/src/services/serialization/resource/resource-serialization.service.spec.ts +0 -311
  73. package/src/services/serialization/resource/resource-serialization.service.ts +0 -166
  74. package/src/services/state-management/local.state-provider.spec.ts +0 -42
  75. package/src/services/state-management/local.state-provider.ts +0 -23
  76. package/src/services/state-management/state-management.service.ts +0 -43
  77. package/src/services/state-management/state-provider.interface.ts +0 -5
  78. package/src/services/transaction/__snapshots__/transaction.service.spec.ts.snap +0 -284
  79. package/src/services/transaction/transaction.service.spec.ts +0 -823
  80. package/src/services/transaction/transaction.service.ts +0 -386
  81. package/test/app.e2e-spec.ts +0 -104
  82. package/test/helpers/test-classes.ts +0 -31
  83. package/test/models/__snapshots__/model.e2e-spec.ts.snap +0 -451
  84. package/test/models/model.e2e-spec.ts +0 -366
  85. package/tsconfig.build.json +0 -4
  86. package/tsconfig.json +0 -9
@@ -1,62 +0,0 @@
1
- import { resolve } from 'path';
2
- import { UnknownModel } from '../../app.type.js';
3
- import { Model } from '../../decorators/model.decorator.js';
4
- import { AModel } from '../model.abstract.js';
5
- import { IImage } from './image.interface.js';
6
-
7
- export interface IImageDockerOptions {
8
- dockerfilePath: string;
9
- buildArgs?: { [key: string]: string };
10
- quiet?: boolean;
11
- }
12
-
13
- @Model()
14
- export class Image extends AModel<IImage, Image> {
15
- readonly MODEL_NAME: string = 'image';
16
-
17
- readonly dockerOptions: IImageDockerOptions;
18
-
19
- readonly imageId: string;
20
-
21
- readonly imageName: string;
22
-
23
- readonly imageTag: string;
24
-
25
- constructor(imageName: string, imageTag: string, options: IImageDockerOptions) {
26
- super();
27
- this.imageId = `${imageName}:${imageTag}`;
28
- this.imageName = imageName;
29
- this.imageTag = imageTag;
30
-
31
- options.dockerfilePath = resolve(options.dockerfilePath);
32
- this.dockerOptions = options;
33
- }
34
-
35
- getContext(): string {
36
- const parents = this.getParents();
37
- const app = parents['app'][0].to;
38
- return [`${this.MODEL_NAME}=${this.imageId}`, app.getContext()].join(',');
39
- }
40
-
41
- synth(): IImage {
42
- return {
43
- dockerOptions: {
44
- buildArgs: { ...this.dockerOptions.buildArgs },
45
- dockerfilePath: this.dockerOptions.dockerfilePath,
46
- quiet: this.dockerOptions.quiet,
47
- },
48
- imageId: this.imageTag,
49
- imageName: this.imageName,
50
- imageTag: this.imageTag,
51
- };
52
- }
53
-
54
- static override async unSynth(
55
- image: IImage,
56
- deReferenceContext: (context: string) => Promise<UnknownModel>,
57
- ): Promise<Image> {
58
- return new Image(image.imageName, image.imageTag, {
59
- ...image.dockerOptions,
60
- });
61
- }
62
- }
@@ -1,324 +0,0 @@
1
- import { ModelType, UnknownModel } from '../app.type.js';
2
- import { Dependency } from '../functions/dependency/dependency.model.js';
3
- import { Diff, DiffAction } from '../functions/diff/diff.model.js';
4
- import { DiffUtility } from '../functions/diff/diff.utility.js';
5
- import { AAnchor } from '../overlays/anchor.abstract.js';
6
- import { IModel } from './model.interface.js';
7
-
8
- /**
9
- * This is the base implementation of the Model's interface,
10
- * and is used to define common functionality between all models.
11
- * All models are an extension of this class.
12
- */
13
- export abstract class AModel<I, T> implements IModel<I, T> {
14
- abstract readonly MODEL_NAME: string;
15
- readonly MODEL_TYPE: ModelType = ModelType.MODEL;
16
-
17
- protected readonly anchors: AAnchor[] = [];
18
-
19
- protected readonly dependencies: Dependency[] = [];
20
-
21
- addChild(onField: keyof T | string, child: UnknownModel, toField: string): void {
22
- // Check if child already has a dependency to self.
23
- const cIndex = child.dependencies.findIndex((d) => Object.is(d.to, this));
24
- if (cIndex !== -1 && child.dependencies[cIndex].isParentRelationship()) {
25
- throw new Error('Found circular dependencies!');
26
- }
27
-
28
- const childToParentDependency = cIndex === -1 ? new Dependency(child, this) : child.dependencies[cIndex];
29
- childToParentDependency.addBehavior(toField, DiffAction.ADD, onField as string, DiffAction.ADD);
30
- childToParentDependency.addBehavior(toField, DiffAction.ADD, onField as string, DiffAction.UPDATE);
31
- if (cIndex === -1) {
32
- childToParentDependency.addChildRelationship(toField, onField as string);
33
- child.dependencies.push(childToParentDependency);
34
- }
35
-
36
- // Check if parent already has a dependency to child.
37
- const pIndex = this.dependencies.findIndex((d) => Object.is(d.to, child));
38
- if (pIndex !== -1 && this.dependencies[pIndex].isChildRelationship()) {
39
- throw new Error('Found circular dependencies!');
40
- }
41
- const parentToChildDependency = pIndex === -1 ? new Dependency(this, child) : this.dependencies[pIndex];
42
- parentToChildDependency.addBehavior(onField as string, DiffAction.DELETE, toField, DiffAction.DELETE);
43
- if (pIndex === -1) {
44
- parentToChildDependency.addParentRelationship(onField as string, toField);
45
- this.dependencies.push(parentToChildDependency);
46
- }
47
- }
48
-
49
- addRelationship(onField: keyof T | string, to: UnknownModel, toField: string): void {
50
- const thisToThatDependency = new Dependency(this, to);
51
- thisToThatDependency.addBehavior(onField as string, DiffAction.ADD, toField, DiffAction.ADD);
52
- thisToThatDependency.addBehavior(onField as string, DiffAction.ADD, toField, DiffAction.UPDATE);
53
- this.dependencies.push(thisToThatDependency);
54
- const thatToThisDependency = new Dependency(to, this);
55
- thatToThisDependency.addBehavior(toField, DiffAction.DELETE, onField as string, DiffAction.DELETE);
56
- to.dependencies.push(thatToThisDependency);
57
- }
58
-
59
- async diff(previous?: T): Promise<Diff[]> {
60
- const childrenByModel = this.getChildren();
61
- const childrenOfPreviousByModel = (previous as UnknownModel)?.getChildren() ?? {};
62
-
63
- const diffs: Diff[] = [];
64
- const modelsSeen: string[] = [];
65
-
66
- for (const modelName in childrenByModel) {
67
- const children = childrenByModel[modelName].map((d) => d.to);
68
- const childrenOfPrevious = childrenOfPreviousByModel[modelName]?.map((d) => d.to) ?? [];
69
- const field = childrenByModel[modelName][0].getRelationship()!.toField;
70
- const childrenDiffs = await DiffUtility.diffModels(childrenOfPrevious, children, field as string);
71
- diffs.push(...childrenDiffs);
72
-
73
- modelsSeen.push(modelName);
74
- }
75
-
76
- for (const modelName in childrenOfPreviousByModel) {
77
- if (modelsSeen.indexOf(modelName) !== -1) {
78
- continue;
79
- }
80
-
81
- const children = [];
82
- const childrenOfPrevious = childrenOfPreviousByModel[modelName].map((d) => d.to);
83
- const field = childrenOfPreviousByModel[modelName][0].getRelationship()!.toField;
84
- const childrenDiffs = await DiffUtility.diffModels(childrenOfPrevious, children, field as string);
85
- diffs.push(...childrenDiffs);
86
- }
87
-
88
- return diffs;
89
- }
90
-
91
- /**
92
- * Get an array of ancestors which must exist for self to exist.
93
- */
94
- getAncestors(): UnknownModel[] {
95
- const members: UnknownModel[] = [this];
96
- const membersProcessed: UnknownModel[] = [];
97
-
98
- while (members.length > 0) {
99
- const member = members.pop() as UnknownModel;
100
-
101
- // Skip processing an already processed member.
102
- if (membersProcessed.some((m) => m.getContext() === member.getContext())) {
103
- continue;
104
- }
105
-
106
- for (const d of member.dependencies) {
107
- // If in dependency I am not declared a parent, then d.to is either my parent or has a relationship to me.
108
- // The behavior of that relationship should be that it must exist for me to exist.
109
- if (
110
- !d.isParentRelationship() &&
111
- d.hasMatchingBehavior(undefined, DiffAction.ADD, undefined, DiffAction.ADD) &&
112
- !membersProcessed.some((m) => m.getContext() === d.to.getContext())
113
- ) {
114
- members.push(d.to);
115
- }
116
- }
117
-
118
- membersProcessed.push(member);
119
- }
120
-
121
- return membersProcessed;
122
- }
123
-
124
- getAnchor(anchorId: string): AAnchor | undefined {
125
- return this.anchors.find((a) => a.anchorId === anchorId);
126
- }
127
-
128
- getAnchors(): AAnchor[] {
129
- return this.anchors;
130
- }
131
-
132
- /**
133
- * Get a boundary (sub graph) of a model, i.e. an array of models that must belong together.
134
- * To generate a boundary, we must process all children and grand-children of self.
135
- */
136
- getBoundaryMembers(): UnknownModel[] {
137
- if (this.MODEL_TYPE !== ModelType.MODEL) {
138
- return [];
139
- }
140
-
141
- const extenders: UnknownModel[] = [this];
142
- const members: UnknownModel[] = [];
143
- const parentOf: { [key: string]: string[] } = {};
144
-
145
- const pushToExtenders = (models: UnknownModel[]): void => {
146
- models.forEach((model) => {
147
- if (model.MODEL_TYPE !== ModelType.MODEL) {
148
- return;
149
- }
150
- if (!members.some((m) => m.getContext() === model.getContext())) {
151
- extenders.push(model);
152
- }
153
- });
154
- };
155
-
156
- while (extenders.length > 0) {
157
- const model = extenders.pop() as UnknownModel;
158
- const ancestors = model.getAncestors();
159
-
160
- for (const ancestor of ancestors) {
161
- // Skip processing an already processed ancestor.
162
- if (members.some((m) => m.getContext() === ancestor.getContext())) {
163
- continue;
164
- }
165
- // Skip processing Non-MODEL ancestors.
166
- if (ancestor.MODEL_TYPE !== ModelType.MODEL) {
167
- continue;
168
- }
169
- members.push(ancestor);
170
-
171
- // Check for circular dependencies on ancestor.
172
- for (const d of ancestor.dependencies) {
173
- let childContext;
174
- let parentContext;
175
- if (d.isParentRelationship()) {
176
- childContext = d.to.getContext();
177
- parentContext = d.from.getContext();
178
- } else if (d.isChildRelationship()) {
179
- childContext = d.from.getContext();
180
- parentContext = d.to.getContext();
181
- } else {
182
- // A relationship that is neither parent or child, will not be reported in circular dependency!
183
- continue;
184
- }
185
-
186
- const seen: { [key: string]: boolean } = {};
187
- const toProcess: string[] = [parentContext];
188
- while (toProcess.length > 0) {
189
- const context = toProcess.pop() as string;
190
- if (seen[context]) {
191
- throw new Error('Found circular dependencies!');
192
- } else {
193
- seen[context] = true;
194
- }
195
- toProcess.push(...(parentOf[context] || []));
196
- }
197
-
198
- if (!parentOf[childContext]) {
199
- parentOf[childContext] = [];
200
- parentOf[childContext].push(parentContext);
201
- } else if (!parentOf[childContext].some((p) => p === parentContext)) {
202
- parentOf[childContext].push(parentContext);
203
- }
204
- }
205
- }
206
-
207
- const children = model.getChildren();
208
- for (const modelName in children) {
209
- pushToExtenders(children[modelName].map((d) => d.to));
210
- }
211
-
212
- const dependents = model.dependencies
213
- .filter((d) => !d.isParentRelationship() && !d.isChildRelationship())
214
- .map((d) => d.to);
215
- pushToExtenders(dependents);
216
- }
217
-
218
- return members;
219
- }
220
-
221
- getChild(modelName: string, filters: { key: string; value: any }[]): UnknownModel | undefined {
222
- const dependency = this.getChildren(modelName)[modelName]?.find((d) =>
223
- filters.every((c) => d.to[c.key] === c.value),
224
- );
225
- return dependency ? dependency.to : undefined;
226
- }
227
-
228
- getChildren(modelName?: string): { [key: string]: Dependency[] } {
229
- return this.dependencies
230
- .filter((d) => d.isParentRelationship() && (modelName ? d.to.MODEL_NAME === modelName : true))
231
- .reduce((accumulator, currentValue) => {
232
- if (!(currentValue.to.MODEL_NAME in accumulator)) {
233
- accumulator[currentValue.to.MODEL_NAME] = [];
234
- }
235
- accumulator[currentValue.to.MODEL_NAME].push(currentValue);
236
- return accumulator;
237
- }, {});
238
- }
239
-
240
- abstract getContext(): string;
241
-
242
- getParents(modelName?: string): { [key: string]: Dependency[] } {
243
- return this.dependencies
244
- .filter((d) => d.isChildRelationship() && (modelName ? d.to.MODEL_NAME === modelName : true))
245
- .reduce((accumulator, currentValue) => {
246
- if (!(currentValue.to.MODEL_NAME in accumulator)) {
247
- accumulator[currentValue.to.MODEL_NAME] = [];
248
- }
249
- accumulator[currentValue.to.MODEL_NAME].push(currentValue);
250
- return accumulator;
251
- }, {});
252
- }
253
-
254
- hasAncestor(model: UnknownModel): boolean {
255
- const modelParts = model
256
- .getContext()
257
- .split(',')
258
- .map((p) => p.split('='));
259
-
260
- const ancestors = this.getAncestors();
261
- return ancestors.some((a) => {
262
- const ancestorParts = a
263
- .getContext()
264
- .split(',')
265
- .reduce((map, p) => {
266
- const parts = p.split('=');
267
- map[parts[0]] = parts[1];
268
- return map;
269
- }, {});
270
-
271
- return modelParts.every((p) => ancestorParts[p[0]] === p[1]);
272
- });
273
- }
274
-
275
- remove(ignoreDirectRelationships = false, dryRun = false): void {
276
- // Verify model can be removed.
277
- for (const dependency of this.dependencies) {
278
- // When direct relationship is not ignored, this model can't be a parent or have direct relationships.
279
- // When direct relationship is ignored, this model can't be a parent.
280
- if (
281
- (!ignoreDirectRelationships && !dependency.isChildRelationship()) ||
282
- (ignoreDirectRelationships && dependency.isParentRelationship())
283
- ) {
284
- throw new Error('Cannot remove model until dependent models exist!');
285
- }
286
- }
287
-
288
- if (dryRun) {
289
- return;
290
- }
291
-
292
- // Removing all dependencies that points to this.
293
- for (const dependency of this.dependencies) {
294
- const index = dependency.to.dependencies.findIndex((d) => d.to.getContext() === this.getContext());
295
- dependency.to.dependencies.splice(index, 1);
296
- }
297
- }
298
-
299
- removeRelationship(model: UnknownModel): void {
300
- for (let i = this.dependencies.length - 1; i >= 0; i--) {
301
- const dependency = this.dependencies[i];
302
- if (dependency.to.getContext() === model.getContext()) {
303
- this.dependencies.splice(i, 1);
304
- }
305
- }
306
-
307
- for (let i = model.dependencies.length - 1; i >= 0; i--) {
308
- const dependency = model.dependencies[i];
309
- if (dependency.to.getContext() === this.getContext()) {
310
- model.dependencies.splice(i, 1);
311
- }
312
- }
313
- }
314
-
315
- abstract synth(): I;
316
-
317
- static async unSynth(...args: unknown[]): Promise<UnknownModel> {
318
- if (args.length > 4) {
319
- throw new Error('Too many args in unSynth()');
320
- }
321
-
322
- throw new Error('Method not implemented! Use derived class implementation');
323
- }
324
- }
@@ -1,42 +0,0 @@
1
- import { ModelType } from '../app.type.js';
2
- import { Diff } from '../functions/diff/diff.model.js';
3
-
4
- /**
5
- * Models are the building blocks that can be combined to create any infrastructure.
6
- * This interface outlines the basic structure that any model must have.
7
- * Models have fields (class properties), and functions.
8
- */
9
- export interface IModel<I, T> {
10
- /**
11
- * This is the unique name of the model.
12
- * It is used to identify other instances of the same model.
13
- */
14
- readonly MODEL_NAME: string;
15
-
16
- readonly MODEL_TYPE: ModelType;
17
-
18
- /**
19
- * Generate a diff comparing all children of self with previous instance.
20
- * If previous does not exist, diff adds all children of self.
21
- *
22
- * Diff captures change. So fields that do not change, e.g. ID fields, do not need to be diffed.
23
- */
24
- diff(previous?: T): Promise<Diff[]>;
25
-
26
- /**
27
- * Generates a string representation of self, used to identify the uniqueness of the instance.
28
- */
29
- getContext(): string;
30
-
31
- /**
32
- * Generate a serializable representation of self as per model's interface.
33
- */
34
- synth(): I;
35
- }
36
-
37
- /**
38
- * Model Reference encapsulates identification information of self.
39
- */
40
- export interface IModelReference {
41
- context: string;
42
- }
@@ -1,6 +0,0 @@
1
- import { Pipeline } from './pipeline.model.js';
2
-
3
- export interface IPipeline {
4
- instructionSet: Pipeline['instructionSet'];
5
- pipelineName: Pipeline['pipelineName'];
6
- }
@@ -1,47 +0,0 @@
1
- import { UnknownModel } from '../../app.type.js';
2
- import { Model } from '../../decorators/model.decorator.js';
3
- import { DiffUtility } from '../../functions/diff/diff.utility.js';
4
- import { AModel } from '../model.abstract.js';
5
- import { Diff } from '../../functions/diff/diff.model.js';
6
- import { IPipeline } from './pipeline.interface.js';
7
-
8
- @Model()
9
- export class Pipeline extends AModel<IPipeline, Pipeline> {
10
- readonly MODEL_NAME: string = 'pipeline';
11
-
12
- readonly instructionSet: string[] = [];
13
-
14
- readonly pipelineName: string;
15
-
16
- constructor(pipelineName: string) {
17
- super();
18
- this.pipelineName = pipelineName;
19
- }
20
-
21
- override async diff(previous?: Pipeline): Promise<Diff[]> {
22
- // Generate diff of instructionSet.
23
- return DiffUtility.diffArray(previous || ({ instructionSet: [] } as unknown as Pipeline), this, 'instructionSet');
24
- }
25
-
26
- getContext(): string {
27
- const parents = this.getParents();
28
- const app = parents['app'][0].to;
29
- return [`${this.MODEL_NAME}=${this.pipelineName}`, app.getContext()].join(',');
30
- }
31
-
32
- synth(): IPipeline {
33
- return {
34
- instructionSet: [...this.instructionSet],
35
- pipelineName: this.pipelineName,
36
- };
37
- }
38
-
39
- static override async unSynth(
40
- pipeline: IPipeline,
41
- deReferenceContext: (context: string) => Promise<UnknownModel>,
42
- ): Promise<Pipeline> {
43
- const newPipeline = new Pipeline(pipeline.pipelineName);
44
- newPipeline.instructionSet.push(...pipeline.instructionSet);
45
- return newPipeline;
46
- }
47
- }
@@ -1,5 +0,0 @@
1
- import { Region } from './region.model.js';
2
-
3
- export interface IRegion {
4
- regionId: Region['regionId'];
5
- }
@@ -1,162 +0,0 @@
1
- import { Environment } from '../environment/environment.model.js';
2
- import { Region } from './region.model.js';
3
-
4
- describe('Region UT', () => {
5
- describe('addEnvironment()', () => {
6
- it('should throw error if duplicate environments exist', () => {
7
- const t = (): void => {
8
- const region = new Region('region-1');
9
- region.addEnvironment(new Environment('qa'));
10
- region.addEnvironment(new Environment('qa'));
11
- };
12
- expect(t).toThrowError('Environment already exists!');
13
- });
14
- });
15
-
16
- describe('diff()', () => {
17
- describe('when diff of environment', () => {
18
- it('should capture addition', async () => {
19
- const region0 = new Region('region-1');
20
-
21
- const region1 = new Region('region-1');
22
- const environment1 = new Environment('qa');
23
- environment1.environmentVariables.set('key1', 'value 1');
24
- environment1.environmentVariables.set('key2', 'value 2');
25
- region1.addEnvironment(environment1);
26
-
27
- const diff = await region1.diff(region0);
28
-
29
- expect(diff).toMatchInlineSnapshot(`
30
- [
31
- {
32
- "action": "add",
33
- "field": "environmentName",
34
- "value": "qa",
35
- },
36
- {
37
- "action": "add",
38
- "field": "environmentVariables",
39
- "value": {
40
- "key": "key1",
41
- "value": "value 1",
42
- },
43
- },
44
- {
45
- "action": "add",
46
- "field": "environmentVariables",
47
- "value": {
48
- "key": "key2",
49
- "value": "value 2",
50
- },
51
- },
52
- ]
53
- `);
54
- });
55
-
56
- it('should capture update', async () => {
57
- const region0 = new Region('region-1');
58
- region0.addEnvironment(new Environment('qa'));
59
-
60
- const region1 = new Region('region-1');
61
- const environment1 = new Environment('qa');
62
- environment1.environmentVariables.set('key', 'value');
63
- region1.addEnvironment(environment1);
64
-
65
- const diff = await region1.diff(region0);
66
-
67
- expect(diff).toMatchInlineSnapshot(`
68
- [
69
- {
70
- "action": "add",
71
- "field": "environmentVariables",
72
- "value": {
73
- "key": "key",
74
- "value": "value",
75
- },
76
- },
77
- ]
78
- `);
79
- });
80
-
81
- it('should capture replace', async () => {
82
- const region0 = new Region('region-1');
83
- region0.addEnvironment(new Environment('qa'));
84
-
85
- const region1 = new Region('aws-ap-south-1');
86
- region1.addEnvironment(new Environment('staging'));
87
-
88
- const diff = await region1.diff(region0);
89
-
90
- expect(diff).toMatchInlineSnapshot(`
91
- [
92
- {
93
- "action": "delete",
94
- "field": "environmentName",
95
- "value": "qa",
96
- },
97
- {
98
- "action": "add",
99
- "field": "environmentName",
100
- "value": "staging",
101
- },
102
- ]
103
- `);
104
- });
105
-
106
- it('should capture deletion', async () => {
107
- const region0 = new Region('region-1');
108
- region0.addEnvironment(new Environment('qa'));
109
-
110
- const region1 = new Region('region-1');
111
-
112
- const diff = await region1.diff(region0);
113
-
114
- expect(diff).toMatchInlineSnapshot(`
115
- [
116
- {
117
- "action": "delete",
118
- "field": "environmentName",
119
- "value": "qa",
120
- },
121
- ]
122
- `);
123
- });
124
-
125
- it('should capture diff without a previous instance', async () => {
126
- const region = new Region('region-1');
127
- const environment = new Environment('qa');
128
- environment.environmentVariables.set('key1', 'value 1');
129
- environment.environmentVariables.set('key2', 'value 2');
130
- region.addEnvironment(environment);
131
-
132
- const diff = await region.diff();
133
-
134
- expect(diff).toMatchInlineSnapshot(`
135
- [
136
- {
137
- "action": "add",
138
- "field": "environmentName",
139
- "value": "qa",
140
- },
141
- {
142
- "action": "add",
143
- "field": "environmentVariables",
144
- "value": {
145
- "key": "key1",
146
- "value": "value 1",
147
- },
148
- },
149
- {
150
- "action": "add",
151
- "field": "environmentVariables",
152
- "value": {
153
- "key": "key2",
154
- "value": "value 2",
155
- },
156
- },
157
- ]
158
- `);
159
- });
160
- });
161
- });
162
- });
@@ -1,48 +0,0 @@
1
- import { UnknownModel } from '../../app.type.js';
2
- import { Model } from '../../decorators/model.decorator.js';
3
- import { Environment } from '../environment/environment.model.js';
4
- import { AModel } from '../model.abstract.js';
5
- import { IRegion } from './region.interface.js';
6
-
7
- @Model()
8
- export class Region extends AModel<IRegion, Region> {
9
- readonly MODEL_NAME: string = 'region';
10
-
11
- readonly regionId: string;
12
-
13
- constructor(regionId: string) {
14
- super();
15
- this.regionId = regionId;
16
- }
17
-
18
- addEnvironment(environment: Environment): void {
19
- const childrenDependencies = this.getChildren('environment');
20
- if (!childrenDependencies['environment']) childrenDependencies['environment'] = [];
21
-
22
- // Check for duplicates.
23
- const environments = childrenDependencies['environment'].map((d) => d.to);
24
- if (environments.find((e: Environment) => e.environmentName === environment.environmentName)) {
25
- throw new Error('Environment already exists!');
26
- }
27
- this.addChild('regionId', environment, 'environmentName');
28
- }
29
-
30
- getContext(): string {
31
- const parents = this.getParents();
32
- const app = parents['app'][0].to;
33
- return [`${this.MODEL_NAME}=${this.regionId}`, app.getContext()].join(',');
34
- }
35
-
36
- synth(): IRegion {
37
- return {
38
- regionId: this.regionId,
39
- };
40
- }
41
-
42
- static override async unSynth(
43
- region: IRegion,
44
- deReferenceContext: (context: string) => Promise<UnknownModel>,
45
- ): Promise<Region> {
46
- return new Region(region.regionId);
47
- }
48
- }