@contrail/cli 3.0.0 → 3.0.2

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/README.md CHANGED
@@ -17,7 +17,7 @@ $ npm install -g @contrail/cli
17
17
  $ contrail COMMAND
18
18
  running command...
19
19
  $ contrail (--version)
20
- @contrail/cli/3.0.0 darwin-x64 node-v18.16.0
20
+ @contrail/cli/3.0.2 darwin-arm64 node-v22.11.0
21
21
  $ contrail --help [COMMAND]
22
22
  USAGE
23
23
  $ contrail COMMAND
@@ -64,7 +64,7 @@ function loadOptionSets(fromDir) {
64
64
  function parseOptionSetValuesToString(optionSet) {
65
65
  return (0, lodash_1.cloneDeepWith)(optionSet, (value) => {
66
66
  // If the value is an object or array, let cloneDeepWith handle it
67
- if ((0, lodash_1.isObject)(value) || (0, lodash_1.isArray)(value)) {
67
+ if ((0, lodash_1.isObject)(value) || (0, lodash_1.isArray)(value) || typeof value === 'boolean') {
68
68
  return undefined;
69
69
  }
70
70
  // Otherwise, convert the value to string
@@ -74,6 +74,7 @@ function parseOptionSetValuesToString(optionSet) {
74
74
  exports.parseOptionSetValuesToString = parseOptionSetValuesToString;
75
75
  async function uploadOS(dir) {
76
76
  const optionSets = loadOptionSets(dir);
77
+ validateOptionSets(optionSets);
77
78
  const currentOptionSets = await new sdk_1.Entities().get({
78
79
  entityName: 'type-property-option-set',
79
80
  });
@@ -126,6 +127,18 @@ async function uploadOS(dir) {
126
127
  logger_1.default.error(`Error Uploading OptionSets: ${err}`);
127
128
  }
128
129
  }
130
+ function validateOptionSets(optionSets) {
131
+ optionSets.forEach((optionSet) => {
132
+ optionSet.optionSet.forEach((option) => {
133
+ validateOption(option, optionSet.slug);
134
+ });
135
+ });
136
+ }
137
+ function validateOption(option, slug) {
138
+ if (typeof option.disabled !== 'boolean') {
139
+ throw new Error(`Invalid type for "disabled" property: expected a boolean but received "${typeof option.disabled}" for Option Set with slug "${slug}" and value "${option.value}".`);
140
+ }
141
+ }
129
142
  // for (const currentOptionSet of currentOptionSets) {
130
143
  // try {
131
144
  // const found = newOptionSetsBySlug[currentOptionSet.slug];
@@ -23,14 +23,17 @@ class LoadValidators {
23
23
  if (!config?.federatedMappings?.name) {
24
24
  errors.push('"name" is required in the config file');
25
25
  }
26
- if (!config?.conditionalColumns?.length) {
27
- errors.push('Conditional columns with "toProperty": "typePath" and "default": "color" properties are required in the config file');
28
- }
29
- if (config?.conditionalColumns &&
26
+ if (config?.conditionalColumns?.length &&
30
27
  config?.conditionalColumns[0].toProperty === 'typePath' &&
31
28
  config?.conditionalColumns[0].default !== 'color') {
32
29
  errors.push('"typePath" must be "color" in the config file');
33
30
  }
31
+ else if (csvHeaders.includes('typePath')) {
32
+ const hasCorrectTypePath = colorsData.every((row) => row.typePath === 'color');
33
+ if (!hasCorrectTypePath) {
34
+ errors.push('"typePath" must be "color" in each row in the csv file');
35
+ }
36
+ }
34
37
  if (!csvHeaders.includes(config?.federatedMappings?.federatedId)) {
35
38
  errors.push(`Federated ID column (${config?.federatedMappings?.federatedId}) not found in the CSV file`);
36
39
  }
@@ -74,9 +77,6 @@ class LoadValidators {
74
77
  if (!config?.federatedMappings?.name) {
75
78
  errors.push('"name" is required in the config file');
76
79
  }
77
- if (!config?.conditionalColumns?.length) {
78
- errors.push('"typePath" is required in the config file');
79
- }
80
80
  if (!csvHeaders.includes(config?.federatedMappings?.federatedId)) {
81
81
  errors.push(`Federated ID column (${config?.federatedMappings?.federatedId}) not found in the CSV file`);
82
82
  }
@@ -87,6 +87,23 @@ class LoadValidators {
87
87
  if (duplicateFederatedIdErrors.length) {
88
88
  errors.push(`Duplicate FederatedIds found: ${duplicateFederatedIdErrors.join(', ')}`);
89
89
  }
90
+ if (Array.isArray(config?.conditionalColumns) && config.conditionalColumns.length) {
91
+ const hasRequiredConditionalColumn = config.conditionalColumns.some((column) => column.toProperty === 'typePath' &&
92
+ column.default?.startsWith('custom-entity:') &&
93
+ column.default?.split(':')[1]?.length > 0);
94
+ if (!hasRequiredConditionalColumn) {
95
+ errors.push('"typePath" must be "custom-entity:<subtypepath>" in the config file');
96
+ }
97
+ }
98
+ else if (csvHeaders.includes('typePath')) {
99
+ const hasCorrectTypePath = customEntityData.every((row) => row.typePath?.startsWith('custom-entity:') && row.typePath?.split(':')[1]?.length > 0);
100
+ if (!hasCorrectTypePath) {
101
+ errors.push('"typePath" must be "custom-entity:<subtypepath>" in each row in the csv file');
102
+ }
103
+ }
104
+ else {
105
+ errors.push('Missing "typePath" column in the csv file or config file');
106
+ }
90
107
  return { errors, warnings };
91
108
  }
92
109
  static ValidateLineSheetLoadFile(lineSheetData, config) {
@@ -3,14 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const validators_1 = require("./validators");
4
4
  describe('LoadValidators', () => {
5
5
  describe('ValidateColorsLoadFile', () => {
6
- it('should return multiple errors if properties are missing in the config file', () => {
7
- const colorsData = [{ id: 1, name: 'Red', hex: '#FF0000' }];
8
- const config = { federatedMappings: { name: 'Colors' } };
9
- const result = validators_1.LoadValidators.ValidateColorsLoadFile(colorsData, config);
10
- expect(result.errors).toContain('"federatedId" is required in the config file');
11
- expect(result.errors).toContain('"loadType" is required in the config file');
12
- expect(result.errors).toContain('Conditional columns with "toProperty": "typePath" and "default": "color" properties are required in the config file');
13
- });
14
6
  it('should return errors if Federated ID is missing in the config file', () => {
15
7
  const colorsData = [{ id: 1, name: 'Red', hex: '#FF0000' }];
16
8
  const config = {
@@ -116,6 +108,38 @@ describe('LoadValidators', () => {
116
108
  const result = validators_1.LoadValidators.ValidateColorsLoadFile(colorsData, config);
117
109
  expect(result.errors).toContain('Duplicate FederatedIds found: 1');
118
110
  });
111
+ it('should throw error if typePath is present in the csv header but have incorrect typePath', () => {
112
+ const colorsData = [{ name: 'color1', typePath: 'color1' }];
113
+ const config = {
114
+ loadType: ['COLOR'],
115
+ conditionalColumns: [],
116
+ federatedMappings: {
117
+ red: 'red',
118
+ green: 'green',
119
+ blue: 'blue',
120
+ name: 'Colors',
121
+ federatedId: 'id',
122
+ },
123
+ };
124
+ const result = validators_1.LoadValidators.ValidateColorsLoadFile(colorsData, config);
125
+ expect(result.errors).toContain('"typePath" must be "color" in each row in the csv file');
126
+ });
127
+ it('should not throw error if typepath is present in the csv header and have correct typePath', () => {
128
+ const colorsData = [{ name: 'color1', typePath: 'color' }];
129
+ const config = {
130
+ loadType: ['COLOR'],
131
+ conditionalColumns: [],
132
+ federatedMappings: {
133
+ red: 'red',
134
+ green: 'green',
135
+ blue: 'blue',
136
+ name: 'name',
137
+ federatedId: 'name',
138
+ },
139
+ };
140
+ const result = validators_1.LoadValidators.ValidateColorsLoadFile(colorsData, config);
141
+ expect(result.errors).toHaveLength(0);
142
+ });
119
143
  });
120
144
  describe('ValidateCustomEntityLoadFile', () => {
121
145
  it('should return error if config is null', () => {
@@ -150,24 +174,24 @@ describe('LoadValidators', () => {
150
174
  const result = validators_1.LoadValidators.ValidateCustomEntityLoadFile(customEntityData, config);
151
175
  expect(result.errors).toContain('"name" is required in the config file');
152
176
  });
153
- it('should return error if conditionalColumns is missing in config', () => {
154
- const customEntityData = [{ id: 1, name: 'Test' }];
177
+ it('should return error if federatedId column is missing in CSV', () => {
178
+ const customEntityData = [{ name: 'Test' }];
155
179
  const config = {
156
180
  loadType: ['CUSTOM_ENTITY'],
157
181
  federatedMappings: { federatedId: 'id', name: 'name' },
182
+ conditionalColumns: ['typePath'],
158
183
  };
159
184
  const result = validators_1.LoadValidators.ValidateCustomEntityLoadFile(customEntityData, config);
160
- expect(result.errors).toContain('"typePath" is required in the config file');
185
+ expect(result.errors).toContain('Federated ID column (id) not found in the CSV file');
161
186
  });
162
- it('should return error if federatedId column is missing in CSV', () => {
163
- const customEntityData = [{ name: 'Test' }];
187
+ it('should return error if conditionalColumns is missing in config', () => {
188
+ const customEntityData = [{ id: 1, name: 'Test' }];
164
189
  const config = {
165
190
  loadType: ['CUSTOM_ENTITY'],
166
191
  federatedMappings: { federatedId: 'id', name: 'name' },
167
- conditionalColumns: ['typePath'],
168
192
  };
169
193
  const result = validators_1.LoadValidators.ValidateCustomEntityLoadFile(customEntityData, config);
170
- expect(result.errors).toContain('Federated ID column (id) not found in the CSV file');
194
+ expect(result.errors).toContain('Missing "typePath" column in the csv file or config file');
171
195
  });
172
196
  it('should return error if name column is missing in CSV', () => {
173
197
  const customEntityData = [{ id: 1 }];
@@ -184,7 +208,7 @@ describe('LoadValidators', () => {
184
208
  const config = {
185
209
  loadType: ['CUSTOM_ENTITY'],
186
210
  federatedMappings: { federatedId: 'id', name: 'name' },
187
- conditionalColumns: [{ toProperty: 'typePath', default: 'custom-entity' }],
211
+ conditionalColumns: [{ toProperty: 'typePath', default: 'custom-entity:material' }],
188
212
  };
189
213
  const result = validators_1.LoadValidators.ValidateCustomEntityLoadFile(customEntityData, config);
190
214
  expect(result.errors).toHaveLength(0);
@@ -203,6 +227,26 @@ describe('LoadValidators', () => {
203
227
  const result = validators_1.LoadValidators.ValidateCustomEntityLoadFile(customEntityData, config);
204
228
  expect(result.errors).toContain('Duplicate FederatedIds found: 1');
205
229
  });
230
+ it('should throw error if typepath is present in the csv header but have incorrect typePath', () => {
231
+ const customEntityData = [{ name: 'custom-entity1', typePath: 'custom-entity1' }];
232
+ const config = {
233
+ loadType: ['CUSTOM_ENTITY'],
234
+ federatedMappings: { federatedId: 'name', name: 'name' },
235
+ conditionalColumns: [],
236
+ };
237
+ const result = validators_1.LoadValidators.ValidateCustomEntityLoadFile(customEntityData, config);
238
+ expect(result.errors).toContain('"typePath" must be "custom-entity:<subtypepath>" in each row in the csv file');
239
+ });
240
+ it('should not throw error if typepath is present in the csv header but have correct typePath', () => {
241
+ const customEntityData = [{ name: 'custom-entity1', typePath: 'custom-entity:material' }];
242
+ const config = {
243
+ loadType: ['CUSTOM_ENTITY'],
244
+ federatedMappings: { federatedId: 'name', name: 'name' },
245
+ conditionalColumns: [],
246
+ };
247
+ const result = validators_1.LoadValidators.ValidateCustomEntityLoadFile(customEntityData, config);
248
+ expect(result.errors).toHaveLength(0);
249
+ });
206
250
  });
207
251
  describe('ValidateLineSheetLoadFile', () => {
208
252
  it('should return an error if config is empty', () => {
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const uploadOptionSets_1 = require("../../commands/types/uploadOptionSets");
4
4
  describe('parseOptionSetValuesToString', () => {
5
- it('should convert all option set values to strings', () => {
5
+ it('should convert all option set values to strings except for booleans', () => {
6
6
  const input = {
7
7
  value1: 123,
8
8
  value2: true,
@@ -10,7 +10,7 @@ describe('parseOptionSetValuesToString', () => {
10
10
  };
11
11
  const expectedOutput = {
12
12
  value1: '123',
13
- value2: 'true',
13
+ value2: true,
14
14
  value3: { nested: 'object' },
15
15
  };
16
16
  const result = (0, uploadOptionSets_1.parseOptionSetValuesToString)(input);
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.0.0",
2
+ "version": "3.0.2",
3
3
  "commands": {
4
4
  "app:create": {
5
5
  "id": "app:create",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/cli",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "VibeIQ's Contrail Platform CLI.",
5
5
  "bin": {
6
6
  "contrail-cli": "./bin/run",
@@ -141,4 +141,4 @@
141
141
  "coverageDirectory": "../coverage",
142
142
  "testEnvironment": "node"
143
143
  }
144
- }
144
+ }