@codifycli/plugin-core 1.0.0-beta1

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 (152) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.json +30 -0
  3. package/.github/workflows/release.yaml +19 -0
  4. package/.github/workflows/unit-test-ci.yaml +18 -0
  5. package/.prettierrc.json +1 -0
  6. package/bin/build.js +189 -0
  7. package/dist/bin/build.d.ts +1 -0
  8. package/dist/bin/build.js +80 -0
  9. package/dist/bin/deploy-plugin.d.ts +2 -0
  10. package/dist/bin/deploy-plugin.js +8 -0
  11. package/dist/common/errors.d.ts +8 -0
  12. package/dist/common/errors.js +24 -0
  13. package/dist/entities/change-set.d.ts +24 -0
  14. package/dist/entities/change-set.js +152 -0
  15. package/dist/entities/errors.d.ts +4 -0
  16. package/dist/entities/errors.js +7 -0
  17. package/dist/entities/plan-types.d.ts +25 -0
  18. package/dist/entities/plan-types.js +1 -0
  19. package/dist/entities/plan.d.ts +15 -0
  20. package/dist/entities/plan.js +127 -0
  21. package/dist/entities/plugin.d.ts +16 -0
  22. package/dist/entities/plugin.js +80 -0
  23. package/dist/entities/resource-options.d.ts +31 -0
  24. package/dist/entities/resource-options.js +76 -0
  25. package/dist/entities/resource-types.d.ts +11 -0
  26. package/dist/entities/resource-types.js +1 -0
  27. package/dist/entities/resource.d.ts +42 -0
  28. package/dist/entities/resource.js +303 -0
  29. package/dist/entities/stateful-parameter.d.ts +29 -0
  30. package/dist/entities/stateful-parameter.js +46 -0
  31. package/dist/entities/transform-parameter.d.ts +4 -0
  32. package/dist/entities/transform-parameter.js +2 -0
  33. package/dist/errors.d.ts +4 -0
  34. package/dist/errors.js +7 -0
  35. package/dist/index.d.ts +20 -0
  36. package/dist/index.js +26 -0
  37. package/dist/messages/handlers.d.ts +14 -0
  38. package/dist/messages/handlers.js +134 -0
  39. package/dist/messages/sender.d.ts +11 -0
  40. package/dist/messages/sender.js +57 -0
  41. package/dist/plan/change-set.d.ts +53 -0
  42. package/dist/plan/change-set.js +153 -0
  43. package/dist/plan/plan-types.d.ts +23 -0
  44. package/dist/plan/plan-types.js +1 -0
  45. package/dist/plan/plan.d.ts +66 -0
  46. package/dist/plan/plan.js +328 -0
  47. package/dist/plugin/plugin.d.ts +24 -0
  48. package/dist/plugin/plugin.js +200 -0
  49. package/dist/pty/background-pty.d.ts +21 -0
  50. package/dist/pty/background-pty.js +127 -0
  51. package/dist/pty/index.d.ts +50 -0
  52. package/dist/pty/index.js +20 -0
  53. package/dist/pty/promise-queue.d.ts +5 -0
  54. package/dist/pty/promise-queue.js +26 -0
  55. package/dist/pty/seqeuntial-pty.d.ts +17 -0
  56. package/dist/pty/seqeuntial-pty.js +119 -0
  57. package/dist/pty/vitest.config.d.ts +2 -0
  58. package/dist/pty/vitest.config.js +11 -0
  59. package/dist/resource/config-parser.d.ts +11 -0
  60. package/dist/resource/config-parser.js +21 -0
  61. package/dist/resource/parsed-resource-settings.d.ts +47 -0
  62. package/dist/resource/parsed-resource-settings.js +196 -0
  63. package/dist/resource/resource-controller.d.ts +36 -0
  64. package/dist/resource/resource-controller.js +402 -0
  65. package/dist/resource/resource-settings.d.ts +303 -0
  66. package/dist/resource/resource-settings.js +147 -0
  67. package/dist/resource/resource.d.ts +144 -0
  68. package/dist/resource/resource.js +44 -0
  69. package/dist/resource/stateful-parameter.d.ts +165 -0
  70. package/dist/resource/stateful-parameter.js +94 -0
  71. package/dist/scripts/deploy.d.ts +1 -0
  72. package/dist/scripts/deploy.js +2 -0
  73. package/dist/stateful-parameter/stateful-parameter-controller.d.ts +21 -0
  74. package/dist/stateful-parameter/stateful-parameter-controller.js +81 -0
  75. package/dist/stateful-parameter/stateful-parameter.d.ts +144 -0
  76. package/dist/stateful-parameter/stateful-parameter.js +43 -0
  77. package/dist/test.d.ts +1 -0
  78. package/dist/test.js +5 -0
  79. package/dist/utils/codify-spawn.d.ts +29 -0
  80. package/dist/utils/codify-spawn.js +136 -0
  81. package/dist/utils/debug.d.ts +2 -0
  82. package/dist/utils/debug.js +10 -0
  83. package/dist/utils/file-utils.d.ts +23 -0
  84. package/dist/utils/file-utils.js +186 -0
  85. package/dist/utils/functions.d.ts +12 -0
  86. package/dist/utils/functions.js +74 -0
  87. package/dist/utils/index.d.ts +46 -0
  88. package/dist/utils/index.js +271 -0
  89. package/dist/utils/internal-utils.d.ts +12 -0
  90. package/dist/utils/internal-utils.js +74 -0
  91. package/dist/utils/load-resources.d.ts +1 -0
  92. package/dist/utils/load-resources.js +46 -0
  93. package/dist/utils/package-json-utils.d.ts +12 -0
  94. package/dist/utils/package-json-utils.js +34 -0
  95. package/dist/utils/pty-local-storage.d.ts +2 -0
  96. package/dist/utils/pty-local-storage.js +2 -0
  97. package/dist/utils/spawn-2.d.ts +5 -0
  98. package/dist/utils/spawn-2.js +7 -0
  99. package/dist/utils/spawn.d.ts +29 -0
  100. package/dist/utils/spawn.js +124 -0
  101. package/dist/utils/utils.d.ts +18 -0
  102. package/dist/utils/utils.js +86 -0
  103. package/dist/utils/verbosity-level.d.ts +5 -0
  104. package/dist/utils/verbosity-level.js +9 -0
  105. package/package.json +59 -0
  106. package/rollup.config.js +24 -0
  107. package/src/common/errors.test.ts +43 -0
  108. package/src/common/errors.ts +31 -0
  109. package/src/errors.ts +8 -0
  110. package/src/index.test.ts +6 -0
  111. package/src/index.ts +30 -0
  112. package/src/messages/handlers.test.ts +329 -0
  113. package/src/messages/handlers.ts +181 -0
  114. package/src/messages/sender.ts +69 -0
  115. package/src/plan/change-set.test.ts +280 -0
  116. package/src/plan/change-set.ts +236 -0
  117. package/src/plan/plan-types.ts +27 -0
  118. package/src/plan/plan.test.ts +413 -0
  119. package/src/plan/plan.ts +499 -0
  120. package/src/plugin/plugin.test.ts +533 -0
  121. package/src/plugin/plugin.ts +291 -0
  122. package/src/pty/background-pty.test.ts +69 -0
  123. package/src/pty/background-pty.ts +154 -0
  124. package/src/pty/index.test.ts +129 -0
  125. package/src/pty/index.ts +66 -0
  126. package/src/pty/promise-queue.ts +33 -0
  127. package/src/pty/seqeuntial-pty.ts +151 -0
  128. package/src/pty/sequential-pty.test.ts +194 -0
  129. package/src/resource/config-parser.ts +42 -0
  130. package/src/resource/parsed-resource-settings.test.ts +186 -0
  131. package/src/resource/parsed-resource-settings.ts +307 -0
  132. package/src/resource/resource-controller-stateful-mode.test.ts +253 -0
  133. package/src/resource/resource-controller.test.ts +1081 -0
  134. package/src/resource/resource-controller.ts +563 -0
  135. package/src/resource/resource-settings.test.ts +1213 -0
  136. package/src/resource/resource-settings.ts +545 -0
  137. package/src/resource/resource.ts +157 -0
  138. package/src/stateful-parameter/stateful-parameter-controller.test.ts +244 -0
  139. package/src/stateful-parameter/stateful-parameter-controller.ts +111 -0
  140. package/src/stateful-parameter/stateful-parameter.ts +160 -0
  141. package/src/utils/debug.ts +11 -0
  142. package/src/utils/file-utils.test.ts +7 -0
  143. package/src/utils/file-utils.ts +231 -0
  144. package/src/utils/functions.ts +103 -0
  145. package/src/utils/index.ts +340 -0
  146. package/src/utils/internal-utils.test.ts +52 -0
  147. package/src/utils/pty-local-storage.ts +3 -0
  148. package/src/utils/test-utils.test.ts +96 -0
  149. package/src/utils/verbosity-level.ts +11 -0
  150. package/tsconfig.json +26 -0
  151. package/tsconfig.test.json +9 -0
  152. package/vitest.config.ts +10 -0
@@ -0,0 +1,181 @@
1
+ import { Ajv, SchemaObject, ValidateFunction } from 'ajv';
2
+ import addFormats from 'ajv-formats';
3
+ import {
4
+ ApplyRequestDataSchema,
5
+ EmptyResponseDataSchema,
6
+ GetResourceInfoRequestDataSchema,
7
+ GetResourceInfoResponseDataSchema,
8
+ ImportRequestDataSchema,
9
+ ImportResponseDataSchema,
10
+ InitializeRequestDataSchema,
11
+ InitializeResponseDataSchema,
12
+ IpcMessage,
13
+ IpcMessageSchema,
14
+ IpcMessageV2,
15
+ IpcMessageV2Schema,
16
+ MatchRequestDataSchema,
17
+ MatchResponseDataSchema,
18
+ MessageStatus,
19
+ PlanRequestDataSchema,
20
+ PlanResponseDataSchema,
21
+ ResourceSchema,
22
+ SetVerbosityRequestDataSchema,
23
+ ValidateRequestDataSchema,
24
+ ValidateResponseDataSchema
25
+ } from 'codify-schemas';
26
+
27
+ import { SudoError } from '../errors.js';
28
+ import { Plugin } from '../plugin/plugin.js';
29
+
30
+ const SupportedRequests: Record<string, { handler: (plugin: Plugin, data: any) => Promise<unknown>; requestValidator: SchemaObject; responseValidator: SchemaObject }> = {
31
+ 'initialize': {
32
+ handler: async (plugin: Plugin, data: any) => plugin.initialize(data),
33
+ requestValidator: InitializeRequestDataSchema,
34
+ responseValidator: InitializeResponseDataSchema
35
+ },
36
+ 'validate': {
37
+ handler: async (plugin: Plugin, data: any) => plugin.validate(data),
38
+ requestValidator: ValidateRequestDataSchema,
39
+ responseValidator: ValidateResponseDataSchema
40
+ },
41
+ 'getResourceInfo': {
42
+ handler: async (plugin: Plugin, data: any) => plugin.getResourceInfo(data),
43
+ requestValidator: GetResourceInfoRequestDataSchema,
44
+ responseValidator: GetResourceInfoResponseDataSchema
45
+ },
46
+ 'setVerbosityLevel': {
47
+ async handler(plugin: Plugin, data: any) {
48
+ await plugin.setVerbosityLevel(data)
49
+ return null;
50
+ },
51
+ requestValidator: SetVerbosityRequestDataSchema,
52
+ responseValidator: EmptyResponseDataSchema,
53
+ },
54
+ 'match': {
55
+ handler: async (plugin: Plugin, data: any) => plugin.match(data),
56
+ requestValidator: MatchRequestDataSchema,
57
+ responseValidator: MatchResponseDataSchema
58
+ },
59
+ 'import': {
60
+ handler: async (plugin: Plugin, data: any) => plugin.import(data),
61
+ requestValidator: ImportRequestDataSchema,
62
+ responseValidator: ImportResponseDataSchema
63
+ },
64
+ 'plan': {
65
+ handler: async (plugin: Plugin, data: any) => plugin.plan(data),
66
+ requestValidator: PlanRequestDataSchema,
67
+ responseValidator: PlanResponseDataSchema
68
+ },
69
+ 'apply': {
70
+ async handler(plugin: Plugin, data: any) {
71
+ await plugin.apply(data);
72
+ return null;
73
+ },
74
+ requestValidator: ApplyRequestDataSchema,
75
+ responseValidator: EmptyResponseDataSchema
76
+ },
77
+ }
78
+
79
+ export class MessageHandler {
80
+ private ajv: Ajv;
81
+ private readonly plugin: Plugin;
82
+ private messageSchemaValidatorV1: ValidateFunction;
83
+ private messageSchemaValidatorV2: ValidateFunction;
84
+ private requestValidators: Map<string, ValidateFunction>;
85
+ private responseValidators: Map<string, ValidateFunction>;
86
+
87
+ constructor(plugin: Plugin) {
88
+ this.ajv = new Ajv({ strict: true, strictRequired: false });
89
+ addFormats.default(this.ajv);
90
+ this.ajv.addSchema(ResourceSchema);
91
+ this.plugin = plugin;
92
+
93
+ this.messageSchemaValidatorV1 = this.ajv.compile(IpcMessageSchema);
94
+ this.messageSchemaValidatorV2 = this.ajv.compile(IpcMessageV2Schema);
95
+
96
+ this.requestValidators = new Map(
97
+ Object.entries(SupportedRequests)
98
+ .map(([k, v]) => [k, this.ajv.compile(v.requestValidator)])
99
+ )
100
+ this.responseValidators = new Map(
101
+ Object.entries(SupportedRequests)
102
+ .map(([k, v]) => [k, this.ajv.compile(v.responseValidator)])
103
+ )
104
+ }
105
+
106
+ async onMessage(message: unknown): Promise<void> {
107
+ try {
108
+ if (!this.validateMessageV2(message) && !this.validateMessage(message)) {
109
+ throw new Error(`Plugin: ${this.plugin}. Message is malformed: ${JSON.stringify(this.messageSchemaValidatorV1.errors, null, 2)}`);
110
+ }
111
+
112
+ if (!this.requestValidators.has(message.cmd)) {
113
+ throw new Error(`Plugin: ${this.plugin}. Unsupported message: ${message.cmd}`);
114
+ }
115
+
116
+ const requestValidator = this.requestValidators.get(message.cmd)!;
117
+ if (!requestValidator(message.data)) {
118
+ throw new Error(`Plugin: ${this.plugin}. cmd: ${message.cmd}. Malformed message data: ${JSON.stringify(requestValidator.errors, null, 2)}`)
119
+ }
120
+
121
+ const result = await SupportedRequests[message.cmd].handler(this.plugin, message.data);
122
+
123
+ const responseValidator = this.responseValidators.get(message.cmd);
124
+ if (responseValidator && !responseValidator(result)) {
125
+ throw new Error(`Plugin: ${this.plugin.name}. Malformed response data: ${JSON.stringify(responseValidator.errors, null, 2)}. Received ${JSON.stringify(result, null, 2)}`);
126
+ }
127
+
128
+ process.send!({
129
+ cmd: message.cmd + '_Response',
130
+ data: result,
131
+ // @ts-expect-error TS2239
132
+ requestId: message.requestId || undefined,
133
+ status: MessageStatus.SUCCESS,
134
+ })
135
+
136
+ } catch (error: unknown) {
137
+ this.handleErrors(message, error as Error);
138
+ }
139
+ }
140
+
141
+ private validateMessage(message: unknown): message is IpcMessage {
142
+ return this.messageSchemaValidatorV1(message);
143
+ }
144
+
145
+ private validateMessageV2(message: unknown): message is IpcMessageV2 {
146
+ return this.messageSchemaValidatorV2(message);
147
+ }
148
+
149
+ private handleErrors(message: unknown, e: Error) {
150
+ if (!message) {
151
+ return;
152
+ }
153
+
154
+ if (!message.hasOwnProperty('cmd')) {
155
+ return;
156
+ }
157
+
158
+ // @ts-expect-error TS2239
159
+ const cmd = message.cmd + '_Response';
160
+
161
+ if (e instanceof SudoError) {
162
+ return process.send?.({
163
+ cmd,
164
+ // @ts-expect-error TS2239
165
+ requestId: message.requestId || undefined,
166
+ data: `Plugin: '${this.plugin.name}'. Forbidden usage of sudo for command '${e.command}'. Please contact the plugin developer to fix this.`,
167
+ status: MessageStatus.ERROR,
168
+ })
169
+ }
170
+
171
+ const isDebug = process.env.DEBUG?.includes('*') ?? false;
172
+
173
+ process.send?.({
174
+ cmd,
175
+ // @ts-expect-error TS2239
176
+ requestId: message.requestId || undefined,
177
+ data: isDebug ? e.stack : e.message,
178
+ status: MessageStatus.ERROR,
179
+ })
180
+ }
181
+ }
@@ -0,0 +1,69 @@
1
+ import { Ajv } from 'ajv';
2
+ import { IpcMessageV2, IpcMessageV2Schema, MessageCmd, PressKeyToContinueRequestData } from 'codify-schemas';
3
+ import { nanoid } from 'nanoid';
4
+
5
+ const ajv = new Ajv({
6
+ strict: true,
7
+ });
8
+
9
+ /**
10
+ * Send requests to the Codify CLI
11
+ */
12
+ class CodifyCliSenderImpl {
13
+ private readonly validateIpcMessageV2 = ajv.compile(IpcMessageV2Schema);
14
+
15
+ async requestPressKeyToContinuePrompt(message?: string): Promise<void> {
16
+ await this.sendAndWaitForResponse(<IpcMessageV2>{
17
+ cmd: MessageCmd.PRESS_KEY_TO_CONTINUE_REQUEST,
18
+ data: <PressKeyToContinueRequestData>{
19
+ promptMessage: message,
20
+ }
21
+ })
22
+ }
23
+
24
+ async getCodifyCliCredentials(): Promise<string> {
25
+ const data = await this.sendAndWaitForResponse(<IpcMessageV2>{
26
+ cmd: MessageCmd.CODIFY_CREDENTIALS_REQUEST,
27
+ data: {},
28
+ })
29
+
30
+ if (typeof data.data !== 'string') {
31
+ throw new Error('Expected string back from credentials request');
32
+ }
33
+
34
+ return data.data;
35
+ }
36
+
37
+ private async sendAndWaitForResponse(message: IpcMessageV2): Promise<IpcMessageV2> {
38
+ return new Promise((resolve) => {
39
+ const requestId = nanoid(8);
40
+ const listener = (data: IpcMessageV2) => {
41
+ if (data.requestId === requestId) {
42
+ process.removeListener('message', listener);
43
+
44
+ if (!this.validateIpcMessageV2(data)) {
45
+ throw new Error(`Invalid response for request.
46
+ Request:
47
+ ${JSON.stringify(message, null, 2)}
48
+ Response:
49
+ ${JSON.stringify(data, null, 2)}
50
+ Error:
51
+ ${JSON.stringify(this.validateIpcMessageV2.errors, null, 2)}`);
52
+ }
53
+
54
+ resolve(data);
55
+ }
56
+ }
57
+
58
+ process.on('message', listener);
59
+
60
+ const ipcMessage = {
61
+ ...message,
62
+ requestId,
63
+ }
64
+ process.send!(ipcMessage)
65
+ })
66
+ }
67
+ }
68
+
69
+ export const CodifyCliSender = new CodifyCliSenderImpl();
@@ -0,0 +1,280 @@
1
+ import { ChangeSet } from './change-set.js';
2
+ import { ParameterOperation, ResourceOperation } from 'codify-schemas';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { ParsedResourceSettings } from '../resource/parsed-resource-settings.js';
5
+
6
+ describe('Change set tests', () => {
7
+ it ('Correctly diffs two resource configs (modify)', () => {
8
+ const after = {
9
+ propA: 'before',
10
+ propB: 'before'
11
+ }
12
+
13
+ const before = {
14
+ propA: 'after',
15
+ propB: 'after'
16
+ }
17
+
18
+ const cs = ChangeSet.calculateModification(after, before);
19
+ expect(cs.parameterChanges.length).to.eq(2);
20
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
21
+ expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.MODIFY);
22
+ expect(cs.operation).to.eq(ResourceOperation.RECREATE)
23
+ })
24
+
25
+ it ('Correctly diffs two resource configs (add)', () => {
26
+ const after = {
27
+ propA: 'before',
28
+ propB: 'after'
29
+ }
30
+
31
+ const before = {
32
+ propA: 'after',
33
+ }
34
+
35
+ const cs = ChangeSet.calculateModification(after, before);
36
+ expect(cs.parameterChanges.length).to.eq(2);
37
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
38
+ expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.ADD);
39
+ expect(cs.operation).to.eq(ResourceOperation.RECREATE)
40
+
41
+ })
42
+
43
+ it ('Correctly diffs two resource configs (remove)', () => {
44
+ const after = {
45
+ propA: 'after',
46
+ }
47
+
48
+ const before = {
49
+ propA: 'before',
50
+ propB: 'before'
51
+ }
52
+
53
+ const cs = ChangeSet.calculateModification(after, before);
54
+ expect(cs.parameterChanges.length).to.eq(2);
55
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
56
+ expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.REMOVE);
57
+ expect(cs.operation).to.eq(ResourceOperation.RECREATE)
58
+ })
59
+
60
+ it ('Correctly diffs two resource configs (no-op)', () => {
61
+ const after = {
62
+ propA: 'prop',
63
+ }
64
+
65
+ const before = {
66
+ propA: 'prop',
67
+ }
68
+
69
+ const cs = ChangeSet.calculateModification(after, before);
70
+ expect(cs.parameterChanges.length).to.eq(1);
71
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.NOOP);
72
+ expect(cs.operation).to.eq(ResourceOperation.NOOP)
73
+ })
74
+
75
+ it('Correctly diffs two resource configs (create)', () => {
76
+ const cs = ChangeSet.create({
77
+ propA: 'prop',
78
+ propB: 'propB'
79
+ });
80
+
81
+ expect(cs.parameterChanges.length).to.eq(2);
82
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.ADD);
83
+ expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.ADD);
84
+ expect(cs.operation).to.eq(ResourceOperation.CREATE)
85
+ })
86
+
87
+ it('Correctly diffs two resource configs (destory)', () => {
88
+ const cs = ChangeSet.destroy({
89
+ propA: 'prop',
90
+ propB: 'propB',
91
+ });
92
+
93
+ expect(cs.parameterChanges.length).to.eq(2);
94
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.REMOVE);
95
+ expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.REMOVE);
96
+ expect(cs.operation).to.eq(ResourceOperation.DESTROY)
97
+ })
98
+
99
+ it ('handles simple arrays', () => {
100
+ const before = {
101
+ propA: ['a', 'b', 'c'],
102
+ }
103
+
104
+ const after = {
105
+ propA: ['b', 'a', 'c'],
106
+ }
107
+
108
+ const parameterSettings = new ParsedResourceSettings({
109
+ id: 'type',
110
+ parameterSettings: {
111
+ propA: { type: 'array' }
112
+ }
113
+ }).parameterSettings
114
+
115
+ const cs = ChangeSet.calculateModification(after, before, parameterSettings);
116
+ expect(cs.parameterChanges.length).to.eq(1);
117
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.NOOP);
118
+ expect(cs.operation).to.eq(ResourceOperation.NOOP)
119
+ })
120
+
121
+ it('handles simple arrays 2', () => {
122
+ const after = {
123
+ propA: ['a', 'b', 'c'],
124
+ }
125
+
126
+ const before = {
127
+ propA: ['b', 'a'],
128
+ }
129
+
130
+ const parameterSettings = new ParsedResourceSettings({
131
+ id: 'type',
132
+ parameterSettings: {
133
+ propA: { type: 'array' }
134
+ }
135
+ }).parameterSettings
136
+
137
+ const cs = ChangeSet.calculateModification(after, before, parameterSettings);
138
+ expect(cs.parameterChanges.length).to.eq(1);
139
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
140
+ expect(cs.operation).to.eq(ResourceOperation.RECREATE)
141
+ })
142
+
143
+ it('determines the order of operations with canModify 1', () => {
144
+ const after = {
145
+ propA: 'after',
146
+ }
147
+
148
+ const before = {
149
+ propA: 'before',
150
+ propB: 'before'
151
+ }
152
+
153
+ const parameterSettings = new ParsedResourceSettings({
154
+ id: 'type',
155
+ parameterSettings: {
156
+ propA: { canModify: true }
157
+ }
158
+ }).parameterSettings
159
+
160
+ const cs = ChangeSet.calculateModification(after, before, parameterSettings);
161
+ expect(cs.parameterChanges.length).to.eq(2);
162
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
163
+ expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.REMOVE);
164
+ expect(cs.operation).to.eq(ResourceOperation.RECREATE)
165
+ })
166
+
167
+ it('determines the order of operations with canModify 2', () => {
168
+ const after = {
169
+ propA: 'after',
170
+ }
171
+
172
+ const before = {
173
+ propA: 'before',
174
+ propB: 'before'
175
+ }
176
+
177
+ const parameterSettings = new ParsedResourceSettings({
178
+ id: 'type',
179
+ parameterSettings: {
180
+ propA: { canModify: true },
181
+ propB: { canModify: true }
182
+ },
183
+ }).parameterSettings
184
+
185
+ const cs = ChangeSet.calculateModification<any>(after, before, parameterSettings);
186
+ expect(cs.parameterChanges.length).to.eq(2);
187
+ expect(cs.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
188
+ expect(cs.parameterChanges[1].operation).to.eq(ParameterOperation.REMOVE);
189
+ expect(cs.operation).to.eq(ResourceOperation.MODIFY)
190
+ })
191
+
192
+
193
+ it('correctly determines array equality', () => {
194
+ const arrA = ['a', 'b', 'd'];
195
+ const arrB = ['a', 'b', 'd'];
196
+
197
+ const parameterSettings = new ParsedResourceSettings({
198
+ id: 'type',
199
+ parameterSettings: {
200
+ propA: { type: 'array' }
201
+ },
202
+ }).parameterSettings
203
+
204
+
205
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, parameterSettings)
206
+
207
+ expect(result.operation).to.eq(ResourceOperation.NOOP);
208
+ })
209
+
210
+ it('correctly determines array equality 2', () => {
211
+ const arrA = ['a', 'b'];
212
+ const arrB = ['a', 'b', 'd'];
213
+
214
+ const parameterSettings = new ParsedResourceSettings({
215
+ id: 'type',
216
+ parameterSettings: {
217
+ propA: { type: 'array' }
218
+ },
219
+ }).parameterSettings
220
+
221
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, parameterSettings)
222
+
223
+ expect(result.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
224
+ })
225
+
226
+ it('correctly determines array equality 3', () => {
227
+ const arrA = ['b', 'a', 'd'];
228
+ const arrB = ['a', 'b', 'd'];
229
+
230
+ const parameterSettings = new ParsedResourceSettings({
231
+ id: 'type',
232
+ parameterSettings: {
233
+ propA: { type: 'array' }
234
+ },
235
+ }).parameterSettings
236
+
237
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, parameterSettings)
238
+
239
+ expect(result.parameterChanges[0].operation).to.eq(ParameterOperation.NOOP);
240
+ })
241
+
242
+ it('correctly determines array equality 4', () => {
243
+ const arrA = [{ key1: 'a' }, { key1: 'a' }, { key1: 'a' }];
244
+ const arrB = [{ key1: 'a' }, { key1: 'a' }, { key1: 'b' }];
245
+
246
+ const parameterSettings = new ParsedResourceSettings({
247
+ id: 'type',
248
+ parameterSettings: {
249
+ propA: {
250
+ type: 'array',
251
+ isElementEqual: (a, b) => a.key1 === b.key1
252
+ }
253
+ },
254
+ }).parameterSettings
255
+
256
+
257
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, parameterSettings)
258
+
259
+ expect(result.parameterChanges[0].operation).to.eq(ParameterOperation.MODIFY);
260
+ })
261
+
262
+ it('correctly determines array equality 5', () => {
263
+ const arrA = [{ key1: 'b' }, { key1: 'a' }, { key1: 'a' }];
264
+ const arrB = [{ key1: 'a' }, { key1: 'a' }, { key1: 'b' }];
265
+
266
+ const parameterSettings = new ParsedResourceSettings({
267
+ id: 'type',
268
+ parameterSettings: {
269
+ propA: {
270
+ type: 'array',
271
+ isElementEqual: (a, b) => a.key1 === b.key1
272
+ }
273
+ },
274
+ }).parameterSettings
275
+
276
+ const result = ChangeSet.calculateModification({ propA: arrA }, { propA: arrB }, parameterSettings)
277
+
278
+ expect(result.parameterChanges[0].operation).to.eq(ParameterOperation.NOOP);
279
+ })
280
+ })