@suitegeezus/suitecloud-cli 3.1.4 → 3.1.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suitegeezus/suitecloud-cli",
3
- "version": "3.1.4",
3
+ "version": "3.1.5",
4
4
  "license": "UPL-1.0",
5
5
  "description": "SuiteCloud CLI for Node.js",
6
6
  "keywords": [
package/src/CLI.js CHANGED
@@ -148,7 +148,7 @@ module.exports = class CLI {
148
148
  "name": "customflag",
149
149
  "option": "customflag",
150
150
  "description": "A custom boolean that will be passed into the hooks. Has no effect unless you implement the logic in a hook",
151
- "mandatory": false,
151
+ "mandatory": true,
152
152
  "type": "FLAG",
153
153
  "usage": "",
154
154
  "defaultOption": false,
@@ -168,11 +168,21 @@ module.exports = class CLI {
168
168
  "name": "debug",
169
169
  "option": "debug",
170
170
  "description": "Directory to dump debug data for debugging. Creates a unique {command}.{datetime}.json file per run",
171
- "mandatory": false,
171
+ "mandatory": true,
172
172
  "type": "SINGLE",
173
173
  "usage": "\"./debug-output\"",
174
174
  "defaultOption": false,
175
175
  "disableInIntegrationMode": false
176
+ },
177
+ "skiphooks": {
178
+ "name": "skiphooks",
179
+ "option": "skiphooks",
180
+ "description": "Skip hook execution. Values: 'pre' (skip beforeExecuting), 'post' (skip onCompleted/onError), 'all' (skip all hooks)",
181
+ "mandatory": true,
182
+ "type": "SINGLE",
183
+ "usage": "\"pre|post|all|none\"",
184
+ "defaultOption": "none",
185
+ "disableInIntegrationMode": false
176
186
  }
177
187
  }
178
188
  );
@@ -24,7 +24,8 @@ const NEVER_PARAMS = {
24
24
  'NOCONFIG': 'noconfig',
25
25
  'CUSTOMFLAG': 'customflag',
26
26
  'CUSTOMOPTIONS': 'customoptions',
27
- 'DEBUG': 'debug'
27
+ 'DEBUG': 'debug',
28
+ 'SKIPHOOKS': 'skiphooks'
28
29
  };
29
30
 
30
31
  /** @type {import('./Command')} */
@@ -71,7 +72,7 @@ class Command {
71
72
 
72
73
  const preExec = await this._action.preExecute(execParams);
73
74
 
74
- this._validateActionParameters(preExec);
75
+ this._validateActionParameters(preExec, Object.values(NEVER_PARAMS));
75
76
 
76
77
  const exec = await this._action.execute(preExec);
77
78
  const actionResult = await this._action.postExecute(exec);
@@ -91,12 +92,19 @@ class Command {
91
92
  })
92
93
  }
93
94
 
94
- _validateActionParameters(params) {
95
+ _validateActionParameters(params, ignores=[]) {
95
96
  const commandOptionsValidator = new CommandOptionsValidator();
96
- const validationErrors = commandOptionsValidator.validate({
97
- commandOptions: this._commandMetadata.options,
98
- arguments: params,
99
- });
97
+
98
+ // remove NEVER_PARAMS from this validation as they don't get passed down to the exec layer
99
+ const options = ignores.reduce((acc,ignore)=> {
100
+ delete acc.commandOptions[ignore];
101
+ return acc;
102
+ },{
103
+ commandOptions: this._commandMetadata.options,
104
+ arguments: params,
105
+ }
106
+ );
107
+ const validationErrors = commandOptionsValidator.validate(options);
100
108
  if (validationErrors.length > 0) {
101
109
  throwValidationException(validationErrors, this._runInInteractiveMode, this._commandMetadata);
102
110
  }
@@ -52,6 +52,7 @@ module.exports = class CommandActionExecutor {
52
52
  let commandUserExtension;
53
53
  const commandName = context.commandName;
54
54
  const debugFilePath = this._getDebugFilePath(context.arguments.debug, commandName);
55
+ const skipHooks = context.arguments.skiphooks;
55
56
  try {
56
57
  const commandMetadata = this._commandsMetadataService.getCommandMetadataByName(commandName);
57
58
  if (context.arguments.config) {
@@ -68,6 +69,7 @@ module.exports = class CommandActionExecutor {
68
69
  const runInInteractiveMode = context.runInInteractiveMode;
69
70
  const commandArguments = this._extractOptionValuesFromArguments(commandMetadata.options, context.arguments);
70
71
 
72
+
71
73
  // need the
72
74
  const projectFolder = this._cliConfigurationService.getProjectFolder(commandName, commandArguments?.project);
73
75
  const projectPath = this._cliConfigurationService.getProjectPath(projectFolder);
@@ -144,9 +146,12 @@ module.exports = class CommandActionExecutor {
144
146
  process.env[ENV_VARS.SUITECLOUD_PROJECT_ROOT] = this._executionPath;
145
147
  // this might modified but we need the user's hooks to take advantage of their current values
146
148
  process.env[ENV_VARS.SUITECLOUD_AUTHID] = authId;
147
- this._dumpDebugFile(debugFilePath, undefined, 'FIRST');
148
- this._dumpDebugFile(debugFilePath, 'beforeExecuting', beforeExecutingOptions, runInInteractiveMode);
149
- const beforeExecutingOutput = await commandUserExtension.beforeExecuting(beforeExecutingOptions);
149
+ const skipPre = skipHooks === 'pre' || skipHooks === 'all';
150
+ this._dumpDebugFile(debugFilePath, undefined, 'FIRST', runInInteractiveMode, skipPre);
151
+ this._dumpDebugFile(debugFilePath, 'beforeExecuting', beforeExecutingOptions, runInInteractiveMode, skipPre);
152
+ const beforeExecutingOutput = skipPre
153
+ ? { arguments: beforeExecutingOptions.arguments }
154
+ : await commandUserExtension.beforeExecuting(beforeExecutingOptions);
150
155
  const overriddenArguments = beforeExecutingOutput.arguments;
151
156
 
152
157
  // do not allow this argument to be overwritten (i.e. change it back)
@@ -156,7 +161,7 @@ module.exports = class CommandActionExecutor {
156
161
  // update the authid as well (not recommended but possible) -- should this be in the isSetupRequired check?
157
162
  // EXPERIMENTAL: ALLOW UPDATE OF THE AUTHID AT THIS TIME
158
163
  if (commandMetadata.isSetupRequired)
159
- authId = beforeExecutingOutput?.arguments?.authid || authId;
164
+ authId = beforeExecutingOutput?.['arguments']?.authid || authId;
160
165
 
161
166
  if (commandMetadata.isSetupRequired && !context.arguments[AUTHORIZATION_PROPERTIES_KEYS.SKIP_AUHTORIZATION_CHECK]) {
162
167
  // check if reauthz is needed to show proper message before continuing with the execution
@@ -173,6 +178,7 @@ module.exports = class CommandActionExecutor {
173
178
  // command execution
174
179
  // src/commands/Command.js, run(inputParams) => execution flow for all commands
175
180
  const actionResult = await command.run(overriddenArguments);
181
+ const skipPost = skipHooks === 'post' || skipHooks === 'all';
176
182
 
177
183
  if (context.runInInteractiveMode) {
178
184
  // generate non-interactive equivalent
@@ -182,22 +188,23 @@ module.exports = class CommandActionExecutor {
182
188
 
183
189
  if (actionResult.isSuccess() && commandUserExtension.onCompleted) {
184
190
  // run onCompleted(output) from suitecloud.config.js
185
- this._dumpDebugFile(debugFilePath, 'onCompleted', actionResult, runInInteractiveMode);
186
- commandUserExtension.onCompleted(actionResult);
191
+ this._dumpDebugFile(debugFilePath, 'onCompleted', actionResult, runInInteractiveMode, skipPost);
192
+ if (!skipPost) commandUserExtension.onCompleted(actionResult);
187
193
  } else if (!actionResult.isSuccess() && commandUserExtension.onError) {
188
194
  // run onError(error) from suitecloud.config.js
189
195
  const errorData = ActionResultUtils.getErrorMessagesString(actionResult);
190
- this._dumpDebugFile(debugFilePath, 'onError', errorData, runInInteractiveMode);
191
- commandUserExtension.onError(errorData);
196
+ this._dumpDebugFile(debugFilePath, 'onError', errorData, runInInteractiveMode, skipPost);
197
+ if (!skipPost) commandUserExtension.onError(errorData);
192
198
  }
193
199
  return actionResult;
194
200
 
195
201
  } catch (error) {
196
202
  let errorMessage = this._logGenericError(error);
203
+ const skipPostHooksCatch = skipHooks === 'post' || skipHooks === 'all';
197
204
  if (commandUserExtension && commandUserExtension.onError) {
198
205
  // run onError(error) from suitecloud.config.js
199
- this._dumpDebugFile(debugFilePath, 'onError', error, context.runInInteractiveMode);
200
- commandUserExtension.onError(error);
206
+ this._dumpDebugFile(debugFilePath, 'onError', error, context.runInInteractiveMode, skipPostHooksCatch);
207
+ if (!skipPostHooksCatch) commandUserExtension.onError(error);
201
208
  }
202
209
  return ActionResult.Builder.withErrors(Array.isArray(errorMessage) ? errorMessage : [errorMessage]).build();
203
210
  }
@@ -245,8 +252,19 @@ module.exports = class CommandActionExecutor {
245
252
  _extractOptionValuesFromArguments(options, args) {
246
253
  const optionValues = {};
247
254
  for (const optionId in options) {
248
- if (options.hasOwnProperty(optionId) && args.hasOwnProperty(optionId)) {
249
- optionValues[optionId] = args[optionId];
255
+ const optionMeta = options[optionId];
256
+ // apply defaults first
257
+ if (options.hasOwnProperty(optionId)){
258
+ if(optionMeta?.mandatory) {
259
+ if (optionMeta?.type === "FLAG") {
260
+ optionValues[optionId] = Boolean(optionMeta.defaultOption)
261
+ } else if(!["undefined"].includes( typeof optionMeta?.defaultOption)){
262
+ optionValues[optionId] = optionMeta.defaultOption;
263
+ }
264
+ }
265
+ if(args.hasOwnProperty(optionId)) {
266
+ optionValues[optionId] = args[optionId];
267
+ }
250
268
  }
251
269
  }
252
270
 
@@ -297,8 +315,7 @@ module.exports = class CommandActionExecutor {
297
315
  _getDebugFilePath(debugDir, commandName) {
298
316
  if (!debugDir) return null;
299
317
  const sanitizedCommandName = commandName.replace(/:/g, '-');
300
- const datetime = new Date().toISOString().replace(/[:.]/g, '-');
301
- const filename = `${sanitizedCommandName}.${datetime}.json`;
318
+ const filename = `${sanitizedCommandName}.json`;
302
319
  return path.join(debugDir, filename);
303
320
  }
304
321
 
@@ -308,9 +325,10 @@ module.exports = class CommandActionExecutor {
308
325
  * @param hookName
309
326
  * @param {'FIRST'|'LAST'|*} data
310
327
  * @param {boolean} runInInteractiveMode
328
+ * @param {boolean} skipped
311
329
  * @private
312
330
  */
313
- _dumpDebugFile(debugFilePath, hookName, data, runInInteractiveMode = false) {
331
+ _dumpDebugFile(debugFilePath, hookName, data, runInInteractiveMode = false, skipped=false) {
314
332
  if (!debugFilePath || !data ) return;
315
333
  if (data === 'FIRST') {
316
334
  fs.writeFileSync(debugFilePath, '[]');
@@ -323,9 +341,10 @@ module.exports = class CommandActionExecutor {
323
341
  .forEach(key => { envVars[key] = process.env[key]; });
324
342
  const entry = {
325
343
  hook: hookName,
344
+ skipped,
326
345
  timestamp: new Date().toISOString(),
327
346
  env: envVars,
328
- data: data
347
+ data: data,
329
348
  };
330
349
 
331
350
  // Print to console in interactive mode