@hubspot/cli 5.2.1-beta.7 → 5.2.1-beta.9

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 (84) hide show
  1. package/commands/accounts/clean.js +4 -4
  2. package/commands/accounts/info.js +2 -2
  3. package/commands/accounts/list.js +2 -2
  4. package/commands/accounts/rename.js +2 -2
  5. package/commands/accounts.js +2 -2
  6. package/commands/auth.js +4 -6
  7. package/commands/cms/lighthouseScore.js +3 -3
  8. package/commands/cms.js +2 -2
  9. package/commands/config.js +2 -2
  10. package/commands/customObject/schema/create.js +1 -1
  11. package/commands/customObject/schema/update.js +1 -1
  12. package/commands/customObject.js +2 -2
  13. package/commands/fetch.js +5 -5
  14. package/commands/filemanager/fetch.js +3 -3
  15. package/commands/filemanager/upload.js +3 -3
  16. package/commands/filemanager.js +3 -3
  17. package/commands/functions/deploy.js +3 -3
  18. package/commands/functions/list.js +3 -3
  19. package/commands/functions/server.js +3 -3
  20. package/commands/functions.js +2 -2
  21. package/commands/hubdb/clear.js +3 -3
  22. package/commands/hubdb/create.js +3 -3
  23. package/commands/hubdb/delete.js +3 -3
  24. package/commands/hubdb/fetch.js +3 -3
  25. package/commands/hubdb.js +2 -2
  26. package/commands/init.js +4 -6
  27. package/commands/lint.js +2 -2
  28. package/commands/list.js +3 -3
  29. package/commands/logs.js +3 -3
  30. package/commands/module/marketplace-validate.js +4 -4
  31. package/commands/module.js +2 -2
  32. package/commands/mv.js +3 -3
  33. package/commands/open.js +3 -3
  34. package/commands/project/create.js +3 -3
  35. package/commands/project/deploy.js +3 -3
  36. package/commands/project/dev.js +12 -4
  37. package/commands/project/download.js +2 -2
  38. package/commands/project/listBuilds.js +3 -3
  39. package/commands/project/logs.js +3 -3
  40. package/commands/project/migrateApp.js +29 -12
  41. package/commands/project/open.js +4 -4
  42. package/commands/project/upload.js +11 -6
  43. package/commands/project/watch.js +3 -3
  44. package/commands/project.js +2 -2
  45. package/commands/remove.js +3 -3
  46. package/commands/sandbox/create.js +12 -12
  47. package/commands/sandbox/delete.js +4 -4
  48. package/commands/sandbox/sync.js +4 -4
  49. package/commands/sandbox.js +2 -2
  50. package/commands/secrets/addSecret.js +3 -3
  51. package/commands/secrets/deleteSecret.js +3 -3
  52. package/commands/secrets/listSecrets.js +3 -3
  53. package/commands/secrets/updateSecret.js +3 -3
  54. package/commands/secrets.js +2 -2
  55. package/commands/theme/marketplace-validate.js +4 -4
  56. package/commands/theme/preview.js +2 -2
  57. package/commands/upload.js +4 -4
  58. package/commands/watch.js +6 -9
  59. package/jest.config.js +1 -0
  60. package/lang/en.lyaml +34 -36
  61. package/lib/LocalDevManager.js +61 -8
  62. package/lib/__tests__/{commonOpts.js → commonOpts.test.js} +3 -0
  63. package/lib/__tests__/downloadProjectPrompt.test.js +31 -0
  64. package/lib/__tests__/projects.test.js +13 -17
  65. package/lib/__tests__/{serverlessLogs.js → serverlessLogs.test.js} +1 -0
  66. package/lib/buildAccount.js +197 -0
  67. package/lib/developerTestAccounts.js +52 -4
  68. package/lib/localDev.js +31 -19
  69. package/lib/marketplace-validate.js +11 -3
  70. package/lib/projects.js +45 -3
  71. package/lib/prompts/accountNamePrompt.js +81 -0
  72. package/lib/prompts/activeInstallConfirmationPrompt.js +20 -0
  73. package/lib/prompts/createProjectPrompt.js +4 -0
  74. package/lib/prompts/downloadProjectPrompt.js +4 -5
  75. package/lib/prompts/personalAccessKeyPrompt.js +2 -2
  76. package/lib/prompts/sandboxesPrompt.js +12 -41
  77. package/lib/prompts/selectPublicAppPrompt.js +9 -14
  78. package/lib/sandboxes.js +160 -1
  79. package/package.json +4 -4
  80. package/lib/developerTestAccountCreate.js +0 -186
  81. package/lib/prompts/developerTestAccountNamePrompt.js +0 -29
  82. package/lib/prompts/enterAccountNamePrompt.js +0 -33
  83. package/lib/sandboxCreate.js +0 -318
  84. /package/lib/__tests__/{validation.js → validation.test.js} +0 -0
package/lib/localDev.js CHANGED
@@ -8,32 +8,26 @@ const {
8
8
  isSpecifiedError,
9
9
  } = require('@hubspot/local-dev-lib/errors/apiErrors');
10
10
  const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
11
- const { getAccountConfig } = require('@hubspot/local-dev-lib/config');
11
+ const { getAccountConfig, getEnv } = require('@hubspot/local-dev-lib/config');
12
12
  const { createProject } = require('@hubspot/local-dev-lib/api/projects');
13
+ const {
14
+ ENVIRONMENTS,
15
+ } = require('@hubspot/local-dev-lib/constants/environments');
13
16
  const {
14
17
  confirmDefaultAccountPrompt,
15
18
  selectSandboxTargetAccountPrompt,
16
19
  selectDeveloperTestTargetAccountPrompt,
17
20
  confirmUseExistingDeveloperTestAccountPrompt,
18
21
  } = require('./prompts/projectDevTargetAccountPrompt');
19
- const { sandboxNamePrompt } = require('./prompts/sandboxesPrompt');
20
- const {
21
- developerTestAccountNamePrompt,
22
- } = require('./prompts/developerTestAccountNamePrompt');
23
22
  const { confirmPrompt } = require('./prompts/promptUtils');
24
23
  const {
25
24
  validateSandboxUsageLimits,
26
25
  getAvailableSyncTypes,
27
26
  } = require('./sandboxes');
28
- const { buildSandbox } = require('./sandboxCreate');
29
27
  const { syncSandbox } = require('./sandboxSync');
30
28
  const {
31
29
  validateDevTestAccountUsageLimits,
32
30
  } = require('./developerTestAccounts');
33
- const {
34
- buildDeveloperTestAccount,
35
- saveDevTestAccountToConfig,
36
- } = require('./developerTestAccountCreate');
37
31
  const { logErrorInstance } = require('./errorHandlers/standardErrors');
38
32
  const { uiCommandReference, uiLine, uiAccountDescription } = require('./ui');
39
33
  const SpinniesManager = require('./ui/SpinniesManager');
@@ -60,6 +54,8 @@ const {
60
54
  const {
61
55
  PERSONAL_ACCESS_KEY_AUTH_METHOD,
62
56
  } = require('@hubspot/local-dev-lib/constants/auth');
57
+ const { buildNewAccount, saveAccountToConfig } = require('./buildAccount');
58
+ const { hubspotAccountNamePrompt } = require('./prompts/accountNamePrompt');
63
59
 
64
60
  const i18nKey = 'lib.localDev';
65
61
 
@@ -167,9 +163,9 @@ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
167
163
  process.exit(EXIT_CODES.ERROR);
168
164
  }
169
165
  try {
170
- const { name } = await sandboxNamePrompt(
171
- HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX
172
- );
166
+ const { name } = await hubspotAccountNamePrompt({
167
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
168
+ });
173
169
 
174
170
  trackCommandMetadataUsage(
175
171
  'sandbox-create',
@@ -177,9 +173,9 @@ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
177
173
  accountId
178
174
  );
179
175
 
180
- const { result } = await buildSandbox({
176
+ const { result } = await buildNewAccount({
181
177
  name,
182
- type: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
178
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
183
179
  accountConfig,
184
180
  env,
185
181
  });
@@ -246,18 +242,22 @@ const createDeveloperTestAccountForLocalDev = async (
246
242
  }
247
243
 
248
244
  try {
249
- const { name } = await developerTestAccountNamePrompt(currentPortalCount);
245
+ const { name } = await hubspotAccountNamePrompt({
246
+ currentPortalCount,
247
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST,
248
+ });
250
249
  trackCommandMetadataUsage(
251
250
  'developer-test-account-create',
252
251
  { step: 'project-dev' },
253
252
  accountId
254
253
  );
255
254
 
256
- const { result } = await buildDeveloperTestAccount({
255
+ const { result } = await buildNewAccount({
257
256
  name,
257
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST,
258
258
  accountConfig,
259
259
  env,
260
- maxTestPortals,
260
+ portalLimit: maxTestPortals,
261
261
  });
262
262
 
263
263
  return result.id;
@@ -286,7 +286,11 @@ const useExistingDevTestAccount = async (env, account) => {
286
286
  logger.log('');
287
287
  process.exit(EXIT_CODES.SUCCESS);
288
288
  }
289
- const devTestAcctConfigName = await saveDevTestAccountToConfig(env, account);
289
+ const devTestAcctConfigName = await saveAccountToConfig({
290
+ env,
291
+ accountName: account.accountName,
292
+ accountId: account.id,
293
+ });
290
294
  logger.success(
291
295
  i18n(`lib.developerTestAccount.create.success.configFileUpdated`, {
292
296
  accountName: devTestAcctConfigName,
@@ -426,6 +430,13 @@ const createInitialBuildForNewProject = async (
426
430
  return initialUploadResult.buildResult;
427
431
  };
428
432
 
433
+ const getAccountHomeUrl = accountId => {
434
+ const baseUrl = getHubSpotWebsiteOrigin(
435
+ getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
436
+ );
437
+ return `${baseUrl}/home?portalId=${accountId}`;
438
+ };
439
+
429
440
  module.exports = {
430
441
  confirmDefaultAccountIsTarget,
431
442
  checkIfAppDeveloperAccount,
@@ -436,4 +447,5 @@ module.exports = {
436
447
  useExistingDevTestAccount,
437
448
  createNewProjectForLocalDev,
438
449
  createInitialBuildForNewProject,
450
+ getAccountHomeUrl,
439
451
  };
@@ -57,12 +57,20 @@ const fetchValidationResults = async (accountId, validationId) => {
57
57
  }
58
58
  };
59
59
 
60
- const processValidationErrors = validationResults => {
60
+ const processValidationErrors = (i18nKey, validationResults) => {
61
61
  if (validationResults.errors.length) {
62
- const { errors } = validationResults;
62
+ const { assetPath, errors } = validationResults;
63
63
 
64
64
  errors.forEach(err => {
65
- logger.error(`${err.context}`);
65
+ if (err.failureReasonType === 'DOWNLOAD_EMPTY') {
66
+ logger.error(
67
+ i18n(`${i18nKey}.errors.invalidPath`, {
68
+ path: assetPath,
69
+ })
70
+ );
71
+ } else {
72
+ logger.error(`${err.context}`);
73
+ }
66
74
  });
67
75
  process.exit(EXIT_CODES.ERROR);
68
76
  }
package/lib/projects.js CHANGED
@@ -699,9 +699,51 @@ const makePollTaskStatusFunc = ({
699
699
 
700
700
  return new Promise((resolve, reject) => {
701
701
  const pollInterval = setInterval(async () => {
702
- const taskStatus = await statusFn(accountId, taskName, taskId).catch(
703
- reject
704
- );
702
+ let taskStatus;
703
+ try {
704
+ taskStatus = await statusFn(accountId, taskName, taskId);
705
+ } catch (e) {
706
+ logApiErrorInstance(
707
+ e,
708
+ new ApiErrorContext({
709
+ accountId,
710
+ projectName: taskName,
711
+ })
712
+ );
713
+ return reject(
714
+ new Error(
715
+ i18n(
716
+ `${i18nKey}.makePollTaskStatusFunc.errorFetchingTaskStatus`,
717
+ {
718
+ taskType:
719
+ statusText.TYPE_KEY === PROJECT_BUILD_TEXT.TYPE_KEY
720
+ ? 'build'
721
+ : 'deploy',
722
+ }
723
+ )
724
+ )
725
+ );
726
+ }
727
+
728
+ if (
729
+ !taskStatus ||
730
+ !taskStatus.status ||
731
+ !taskStatus[statusText.SUBTASK_KEY]
732
+ ) {
733
+ return reject(
734
+ new Error(
735
+ i18n(
736
+ `${i18nKey}.makePollTaskStatusFunc.errorFetchingTaskStatus`,
737
+ {
738
+ taskType:
739
+ statusText.TYPE_KEY === PROJECT_BUILD_TEXT.TYPE_KEY
740
+ ? 'build'
741
+ : 'deploy',
742
+ }
743
+ )
744
+ )
745
+ );
746
+ }
705
747
 
706
748
  const { status, [statusText.SUBTASK_KEY]: subTaskStatus } = taskStatus;
707
749
 
@@ -0,0 +1,81 @@
1
+ const { accountNameExistsInConfig } = require('@hubspot/local-dev-lib/config');
2
+ const { STRING_WITH_NO_SPACES_REGEX } = require('../regex');
3
+ const { promptUser } = require('./promptUtils');
4
+ const { i18n } = require('../lang');
5
+ const {
6
+ HUBSPOT_ACCOUNT_TYPES,
7
+ } = require('@hubspot/local-dev-lib/constants/config');
8
+
9
+ const i18nKey = 'lib.prompts.accountNamePrompt';
10
+
11
+ const getCliAccountNamePromptConfig = defaultName => ({
12
+ name: 'name',
13
+ message: i18n(`${i18nKey}.enterAccountName`),
14
+ default: defaultName,
15
+ validate(val) {
16
+ if (typeof val !== 'string') {
17
+ return i18n(`${i18nKey}.errors.invalidName`);
18
+ } else if (!val.length) {
19
+ return i18n(`${i18nKey}.errors.nameRequired`);
20
+ } else if (!STRING_WITH_NO_SPACES_REGEX.test(val)) {
21
+ return i18n(`${i18nKey}.errors.spacesInName`);
22
+ }
23
+ return accountNameExistsInConfig(val)
24
+ ? i18n(`${i18nKey}.errors.accountNameExists`, { name: val })
25
+ : true;
26
+ },
27
+ });
28
+
29
+ const cliAccountNamePrompt = defaultName => {
30
+ return promptUser(getCliAccountNamePromptConfig(defaultName));
31
+ };
32
+
33
+ const hubspotAccountNamePrompt = ({ accountType, currentPortalCount = 0 }) => {
34
+ const isDevelopmentSandbox =
35
+ accountType === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX;
36
+ const isSandbox =
37
+ accountType === HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX ||
38
+ isDevelopmentSandbox;
39
+ const isDeveloperTestAccount =
40
+ accountType === HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST;
41
+
42
+ let promptMessageString;
43
+ let defaultName;
44
+ if (isSandbox) {
45
+ promptMessageString = isDevelopmentSandbox
46
+ ? i18n(`${i18nKey}.enterDevelopmentSandboxName`)
47
+ : i18n(`${i18nKey}.enterStandardSandboxName`);
48
+ defaultName = i18n(`${i18nKey}.sandboxDefaultName`, {
49
+ sandboxType: isDevelopmentSandbox ? 'development' : 'standard',
50
+ });
51
+ } else if (isDeveloperTestAccount) {
52
+ promptMessageString = i18n(`${i18nKey}.enterDeveloperTestAccountName`);
53
+ defaultName = i18n(`${i18nKey}.developerTestAccountDefaultName`, {
54
+ count: currentPortalCount + 1,
55
+ });
56
+ }
57
+
58
+ return promptUser([
59
+ {
60
+ name: 'name',
61
+ message: promptMessageString,
62
+ validate(val) {
63
+ if (typeof val !== 'string') {
64
+ return i18n(`${i18nKey}.errors.invalidName`);
65
+ } else if (!val.length) {
66
+ return i18n(`${i18nKey}.errors.nameRequired`);
67
+ }
68
+ return accountNameExistsInConfig(val)
69
+ ? i18n(`${i18nKey}.errors.accountNameExists`, { name: val })
70
+ : true;
71
+ },
72
+ default: defaultName,
73
+ },
74
+ ]);
75
+ };
76
+
77
+ module.exports = {
78
+ getCliAccountNamePromptConfig,
79
+ cliAccountNamePrompt,
80
+ hubspotAccountNamePrompt,
81
+ };
@@ -0,0 +1,20 @@
1
+ const { promptUser } = require('./promptUtils');
2
+ const { i18n } = require('../lang');
3
+
4
+ const i18nKey = 'lib.prompts.activeInstallConfirmationPrompt';
5
+
6
+ const activeInstallConfirmationPrompt = async () => {
7
+ const { proceed } = await promptUser([
8
+ {
9
+ name: 'proceed',
10
+ message: i18n(`${i18nKey}.message`),
11
+ type: 'confirm',
12
+ default: false,
13
+ },
14
+ ]);
15
+ return proceed;
16
+ };
17
+
18
+ module.exports = {
19
+ activeInstallConfirmationPrompt,
20
+ };
@@ -1,3 +1,4 @@
1
+ const fs = require('fs');
1
2
  const path = require('path');
2
3
  const { getCwd } = require('@hubspot/local-dev-lib/path');
3
4
  const { PROJECT_COMPONENT_TYPES } = require('../../lib/constants');
@@ -84,6 +85,9 @@ const createProjectPrompt = async (
84
85
  if (!input) {
85
86
  return i18n(`${i18nKey}.errors.locationRequired`);
86
87
  }
88
+ if (fs.existsSync(input)) {
89
+ return i18n(`${i18nKey}.errors.invalidLocation`);
90
+ }
87
91
  return true;
88
92
  },
89
93
  },
@@ -10,9 +10,7 @@ const { i18n } = require('../lang');
10
10
 
11
11
  const i18nKey = 'lib.prompts.downloadProjectPrompt';
12
12
 
13
- const createProjectsList = async () => {
14
- const accountId = getAccountId();
15
-
13
+ const createProjectsList = async accountId => {
16
14
  try {
17
15
  const projects = await fetchProjects(accountId);
18
16
  return projects.results;
@@ -23,7 +21,8 @@ const createProjectsList = async () => {
23
21
  };
24
22
 
25
23
  const downloadProjectPrompt = async (promptOptions = {}) => {
26
- const projectsList = await createProjectsList();
24
+ const accountId = getAccountId(promptOptions.account);
25
+ const projectsList = await createProjectsList(accountId);
27
26
 
28
27
  return promptUser([
29
28
  {
@@ -33,7 +32,7 @@ const downloadProjectPrompt = async (promptOptions = {}) => {
33
32
  !projectsList.find(p => p.name === promptOptions.name)
34
33
  ? i18n(`${i18nKey}.errors.projectNotFound`, {
35
34
  projectName: promptOptions.project,
36
- accountId: getAccountId(),
35
+ accountId,
37
36
  })
38
37
  : i18n(`${i18nKey}.selectProject`);
39
38
  },
@@ -7,7 +7,7 @@ const { deleteEmptyConfigFile } = require('@hubspot/local-dev-lib/config');
7
7
  const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
8
8
  const { logger } = require('@hubspot/local-dev-lib/logger');
9
9
  const { promptUser } = require('./promptUtils');
10
- const { accountNamePrompt } = require('./enterAccountNamePrompt');
10
+ const { getCliAccountNamePromptConfig } = require('./accountNamePrompt');
11
11
  const { i18n } = require('../lang');
12
12
  const { uiInfoSection } = require('../ui');
13
13
  const { EXIT_CODES } = require('../enums/exitCodes');
@@ -124,7 +124,7 @@ const SCOPES = {
124
124
  };
125
125
 
126
126
  const OAUTH_FLOW = [
127
- accountNamePrompt(),
127
+ getCliAccountNamePromptConfig(),
128
128
  ACCOUNT_ID,
129
129
  CLIENT_ID,
130
130
  CLIENT_SECRET,
@@ -1,6 +1,5 @@
1
1
  const { promptUser } = require('./promptUtils');
2
2
  const { i18n } = require('../lang');
3
- const { accountNameExistsInConfig } = require('@hubspot/local-dev-lib/config');
4
3
  const { uiAccountDescription } = require('../ui');
5
4
  const {
6
5
  HUBSPOT_ACCOUNT_TYPES,
@@ -29,42 +28,6 @@ const mapNonSandboxAccountChoices = portals =>
29
28
  };
30
29
  });
31
30
 
32
- const sandboxNamePrompt = (type = HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX) => {
33
- const isDevelopmentSandbox =
34
- type === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX;
35
- const namePromptMessage = isDevelopmentSandbox
36
- ? `${i18nKey}.name.developmentSandboxMessage`
37
- : `${i18nKey}.name.message`;
38
- return promptUser([
39
- {
40
- name: 'name',
41
- message: i18n(namePromptMessage),
42
- validate(val) {
43
- if (typeof val !== 'string') {
44
- return i18n(`${i18nKey}.name.errors.invalidName`);
45
- } else if (!val.length) {
46
- return i18n(`${i18nKey}.name.errors.nameRequired`);
47
- }
48
- return accountNameExistsInConfig(val)
49
- ? i18n(`${i18nKey}.name.errors.accountNameExists`, { name: val })
50
- : true;
51
- },
52
- default: `New ${isDevelopmentSandbox ? 'development ' : ''}sandbox`,
53
- },
54
- ]);
55
- };
56
-
57
- const sandboxTypeChoices = [
58
- {
59
- name: i18n(`${i18nKey}.type.developer`),
60
- value: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
61
- },
62
- {
63
- name: i18n(`${i18nKey}.type.standard`),
64
- value: HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
65
- },
66
- ];
67
-
68
31
  const sandboxTypePrompt = () => {
69
32
  return promptUser([
70
33
  {
@@ -72,7 +35,16 @@ const sandboxTypePrompt = () => {
72
35
  message: i18n(`${i18nKey}.type.message`),
73
36
  type: 'list',
74
37
  look: false,
75
- choices: sandboxTypeChoices,
38
+ choices: [
39
+ {
40
+ name: i18n(`${i18nKey}.type.developer`),
41
+ value: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
42
+ },
43
+ {
44
+ name: i18n(`${i18nKey}.type.standard`),
45
+ value: HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
46
+ },
47
+ ],
76
48
  default: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
77
49
  },
78
50
  ]);
@@ -90,8 +62,8 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
90
62
  name: 'account',
91
63
  message: i18n(
92
64
  promptParentAccount
93
- ? `${i18nKey}.name.selectParentAccountName`
94
- : `${i18nKey}.name.selectAccountName`
65
+ ? `${i18nKey}.selectParentAccountName`
66
+ : `${i18nKey}.selectAccountName`
95
67
  ),
96
68
  type: 'list',
97
69
  look: false,
@@ -103,7 +75,6 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
103
75
  };
104
76
 
105
77
  module.exports = {
106
- sandboxNamePrompt,
107
78
  sandboxTypePrompt,
108
79
  deleteSandboxPrompt,
109
80
  };
@@ -35,7 +35,6 @@ const fetchPublicAppOptions = async (accountId, accountName) => {
35
35
  const selectPublicAppPrompt = async ({
36
36
  accountId,
37
37
  accountName,
38
- promptOptions = {},
39
38
  migrateApp = false,
40
39
  }) => {
41
40
  const publicApps = await fetchPublicAppOptions(accountId, accountName);
@@ -44,21 +43,17 @@ const selectPublicAppPrompt = async ({
44
43
  return promptUser([
45
44
  {
46
45
  name: 'appId',
47
- message: () => {
48
- return promptOptions.appId &&
49
- !publicApps.find(a => a.id === promptOptions.appId)
50
- ? i18n(`${i18nKey}.errors.invalidAppId`, {
51
- appId: promptOptions.appId,
52
- })
53
- : i18n(`${i18nKey}.${translationKey}`, {
54
- accountName,
55
- });
56
- },
57
- when:
58
- !promptOptions.appId ||
59
- !publicApps.find(a => a.id === promptOptions.appId),
46
+ message: i18n(`${i18nKey}.${translationKey}`, {
47
+ accountName,
48
+ }),
60
49
  type: 'list',
61
50
  choices: publicApps.map(app => {
51
+ if (app.listingInfo) {
52
+ return {
53
+ name: `${app.name} (${app.id})`,
54
+ disabled: i18n(`${i18nKey}.errors.marketplaceApp`),
55
+ };
56
+ }
62
57
  return {
63
58
  name: `${app.name} (${app.id})`,
64
59
  value: app.id,
package/lib/sandboxes.js CHANGED
@@ -7,7 +7,11 @@ const {
7
7
  fetchTypes,
8
8
  getSandboxUsageLimits,
9
9
  } = require('@hubspot/local-dev-lib/sandboxes');
10
- const { getConfig, getAccountId } = require('@hubspot/local-dev-lib/config');
10
+ const {
11
+ getConfig,
12
+ getAccountId,
13
+ getEnv,
14
+ } = require('@hubspot/local-dev-lib/config');
11
15
  const CliProgressMultibarManager = require('./ui/CliProgressMultibarManager');
12
16
  const { promptUser } = require('./prompts/promptUtils');
13
17
  const { isDevelopmentSandbox } = require('./accountTypes');
@@ -15,6 +19,13 @@ const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
15
19
  const {
16
20
  HUBSPOT_ACCOUNT_TYPES,
17
21
  } = require('@hubspot/local-dev-lib/constants/config');
22
+ const { uiAccountDescription } = require('./ui');
23
+ const {
24
+ isMissingScopeError,
25
+ isSpecifiedError,
26
+ } = require('@hubspot/local-dev-lib/errors/apiErrors');
27
+ const { getValidEnv } = require('@hubspot/local-dev-lib/environment');
28
+ const { logErrorInstance } = require('./errorHandlers/standardErrors');
18
29
 
19
30
  const syncTypes = {
20
31
  OBJECT_RECORDS: 'object-records',
@@ -198,6 +209,153 @@ const validateSandboxUsageLimits = async (accountConfig, sandboxType, env) => {
198
209
  }
199
210
  };
200
211
 
212
+ function handleSandboxCreateError({
213
+ err,
214
+ env,
215
+ accountId,
216
+ name,
217
+ accountConfig,
218
+ }) {
219
+ if (isMissingScopeError(err)) {
220
+ logger.error(
221
+ i18n('lib.sandboxes.create.failure.scopes.message', {
222
+ accountName: uiAccountDescription(accountId),
223
+ })
224
+ );
225
+ const websiteOrigin = getHubSpotWebsiteOrigin(env);
226
+ const url = `${websiteOrigin}/personal-access-key/${accountId}`;
227
+ logger.info(
228
+ i18n('lib.sandboxes.create.failure.scopes.instructions', {
229
+ accountName: uiAccountDescription(accountId),
230
+ url,
231
+ })
232
+ );
233
+ } else if (
234
+ isSpecifiedError(err, {
235
+ statusCode: 403,
236
+ category: 'BANNED',
237
+ subCategory: 'SandboxErrors.USER_ACCESS_NOT_ALLOWED',
238
+ })
239
+ ) {
240
+ logger.log('');
241
+ logger.error(
242
+ i18n('lib.sandboxes.create.failure.invalidUser', {
243
+ accountName: name,
244
+ parentAccountName: uiAccountDescription(accountId),
245
+ })
246
+ );
247
+ logger.log('');
248
+ } else if (
249
+ isSpecifiedError(err, {
250
+ statusCode: 403,
251
+ category: 'BANNED',
252
+ subCategory: 'SandboxErrors.DEVELOPMENT_SANDBOX_ACCESS_NOT_ALLOWED',
253
+ })
254
+ ) {
255
+ logger.log('');
256
+ logger.error(
257
+ i18n('lib.sandboxes.create.failure.403Gating', {
258
+ accountName: name,
259
+ parentAccountName: uiAccountDescription(accountId),
260
+ accountId,
261
+ })
262
+ );
263
+ logger.log('');
264
+ } else if (
265
+ isSpecifiedError(err, {
266
+ statusCode: 400,
267
+ category: 'VALIDATION_ERROR',
268
+ subCategory:
269
+ 'SandboxErrors.NUM_DEVELOPMENT_SANDBOXES_LIMIT_EXCEEDED_ERROR',
270
+ }) &&
271
+ err.error &&
272
+ err.error.message
273
+ ) {
274
+ logger.log('');
275
+ const devSandboxLimit = getSandboxLimit(err.error);
276
+ const plural = devSandboxLimit !== 1;
277
+ const hasDevelopmentSandboxes = getHasSandboxesByType(
278
+ accountConfig,
279
+ HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX
280
+ );
281
+ if (hasDevelopmentSandboxes) {
282
+ logger.error(
283
+ i18n(
284
+ `lib.sandboxes.create.failure.alreadyInConfig.developer.${
285
+ plural ? 'other' : 'one'
286
+ }`,
287
+ {
288
+ accountName: uiAccountDescription(accountId),
289
+ limit: devSandboxLimit,
290
+ }
291
+ )
292
+ );
293
+ } else {
294
+ const baseUrl = getHubSpotWebsiteOrigin(getValidEnv(getEnv(accountId)));
295
+ logger.error(
296
+ i18n(
297
+ `lib.sandboxes.create.failure.limit.developer.${
298
+ plural ? 'other' : 'one'
299
+ }`,
300
+ {
301
+ accountName: uiAccountDescription(accountId),
302
+ limit: devSandboxLimit,
303
+ link: `${baseUrl}/sandboxes-developer/${accountId}/development`,
304
+ }
305
+ )
306
+ );
307
+ }
308
+ logger.log('');
309
+ } else if (
310
+ isSpecifiedError(err, {
311
+ statusCode: 400,
312
+ category: 'VALIDATION_ERROR',
313
+ subCategory: 'SandboxErrors.NUM_STANDARD_SANDBOXES_LIMIT_EXCEEDED_ERROR',
314
+ }) &&
315
+ err.error &&
316
+ err.error.message
317
+ ) {
318
+ logger.log('');
319
+ const standardSandboxLimit = getSandboxLimit(err.error);
320
+ const plural = standardSandboxLimit !== 1;
321
+ const hasStandardSandboxes = getHasSandboxesByType(
322
+ accountConfig,
323
+ HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX
324
+ );
325
+ if (hasStandardSandboxes) {
326
+ logger.error(
327
+ i18n(
328
+ `lib.sandboxes.create.failure.alreadyInConfig.standard.${
329
+ plural ? 'other' : 'one'
330
+ }`,
331
+ {
332
+ accountName: uiAccountDescription(accountId),
333
+ limit: standardSandboxLimit,
334
+ }
335
+ )
336
+ );
337
+ } else {
338
+ const baseUrl = getHubSpotWebsiteOrigin(getValidEnv(getEnv(accountId)));
339
+ logger.error(
340
+ i18n(
341
+ `lib.sandboxes.create.failure.limit.standard.${
342
+ plural ? 'other' : 'one'
343
+ }`,
344
+ {
345
+ accountName: uiAccountDescription(accountId),
346
+ limit: standardSandboxLimit,
347
+ link: `${baseUrl}/sandboxes-developer/${accountId}/standard`,
348
+ }
349
+ )
350
+ );
351
+ }
352
+ logger.log('');
353
+ } else {
354
+ logErrorInstance(err);
355
+ }
356
+ throw err;
357
+ }
358
+
201
359
  const ACTIVE_TASK_POLL_INTERVAL = 1000;
202
360
 
203
361
  const isTaskComplete = task => {
@@ -346,4 +504,5 @@ module.exports = {
346
504
  getAvailableSyncTypes,
347
505
  getSyncTypesWithContactRecordsPrompt,
348
506
  pollSyncTaskStatus,
507
+ handleSandboxCreateError,
349
508
  };