@hubspot/cli 5.2.1-beta.1 → 5.2.1-beta.11

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 (146) hide show
  1. package/bin/cli.js +1 -1
  2. package/bin/hs +1 -1
  3. package/commands/accounts/clean.js +7 -7
  4. package/commands/accounts/info.js +3 -3
  5. package/commands/accounts/list.js +7 -18
  6. package/commands/accounts/remove.js +1 -1
  7. package/commands/accounts/rename.js +3 -3
  8. package/commands/accounts/use.js +1 -1
  9. package/commands/accounts.js +3 -3
  10. package/commands/auth.js +7 -9
  11. package/commands/cms/convertFields.js +1 -1
  12. package/commands/cms/lighthouseScore.js +4 -4
  13. package/commands/cms/reactModules.js +1 -1
  14. package/commands/cms.js +3 -3
  15. package/commands/config/set/allowUsageTracking.js +1 -2
  16. package/commands/config/set/defaultMode.js +1 -1
  17. package/commands/config/set/httpTimeout.js +1 -1
  18. package/commands/config/set.js +1 -1
  19. package/commands/config.js +3 -3
  20. package/commands/create/api-sample.js +1 -1
  21. package/commands/create/module.js +1 -1
  22. package/commands/create/template.js +1 -1
  23. package/commands/create.js +1 -1
  24. package/commands/customObject/create.js +1 -1
  25. package/commands/customObject/schema/create.js +2 -3
  26. package/commands/customObject/schema/delete.js +1 -2
  27. package/commands/customObject/schema/fetch-all.js +1 -2
  28. package/commands/customObject/schema/fetch.js +1 -2
  29. package/commands/customObject/schema/list.js +1 -1
  30. package/commands/customObject/schema/update.js +2 -3
  31. package/commands/customObject/schema.js +1 -1
  32. package/commands/customObject.js +3 -3
  33. package/commands/feedback.js +4 -6
  34. package/commands/fetch.js +6 -6
  35. package/commands/filemanager/fetch.js +4 -4
  36. package/commands/filemanager/upload.js +4 -4
  37. package/commands/filemanager.js +4 -4
  38. package/commands/functions/deploy.js +9 -25
  39. package/commands/functions/list.js +4 -4
  40. package/commands/functions/server.js +4 -4
  41. package/commands/functions.js +3 -3
  42. package/commands/hubdb/clear.js +4 -4
  43. package/commands/hubdb/create.js +4 -4
  44. package/commands/hubdb/delete.js +4 -4
  45. package/commands/hubdb/fetch.js +4 -4
  46. package/commands/hubdb.js +3 -3
  47. package/commands/init.js +6 -8
  48. package/commands/lint.js +3 -3
  49. package/commands/list.js +4 -4
  50. package/commands/logs.js +4 -4
  51. package/commands/module/marketplace-validate.js +5 -5
  52. package/commands/module.js +3 -3
  53. package/commands/mv.js +4 -4
  54. package/commands/open.js +4 -4
  55. package/commands/project/__tests__/deploy.test.js +431 -0
  56. package/commands/project/add.js +1 -1
  57. package/commands/project/cloneApp.js +161 -0
  58. package/commands/project/create.js +4 -4
  59. package/commands/project/deploy.js +79 -25
  60. package/commands/project/dev.js +70 -27
  61. package/commands/project/download.js +11 -7
  62. package/commands/project/listBuilds.js +5 -5
  63. package/commands/project/logs.js +5 -5
  64. package/commands/project/migrateApp.js +244 -0
  65. package/commands/project/open.js +12 -8
  66. package/commands/project/upload.js +13 -8
  67. package/commands/project/watch.js +4 -4
  68. package/commands/project.js +7 -3
  69. package/commands/remove.js +4 -4
  70. package/commands/sandbox/create.js +22 -18
  71. package/commands/sandbox/delete.js +10 -7
  72. package/commands/sandbox/sync.js +8 -8
  73. package/commands/sandbox.js +5 -4
  74. package/commands/secrets/addSecret.js +4 -4
  75. package/commands/secrets/deleteSecret.js +4 -4
  76. package/commands/secrets/listSecrets.js +4 -4
  77. package/commands/secrets/updateSecret.js +4 -4
  78. package/commands/secrets.js +3 -3
  79. package/commands/theme/generate-selectors.js +1 -1
  80. package/commands/theme/marketplace-validate.js +5 -5
  81. package/commands/theme/preview.js +52 -17
  82. package/commands/theme.js +1 -1
  83. package/commands/upload.js +5 -5
  84. package/commands/watch.js +61 -18
  85. package/jest.config.js +1 -0
  86. package/lang/en.lyaml +1450 -1371
  87. package/lib/DevServerManager.js +3 -2
  88. package/lib/LocalDevManager.js +169 -7
  89. package/lib/__tests__/{commonOpts.js → commonOpts.test.js} +3 -0
  90. package/lib/__tests__/downloadProjectPrompt.test.js +31 -0
  91. package/lib/__tests__/projects.test.js +13 -17
  92. package/lib/__tests__/{serverlessLogs.js → serverlessLogs.test.js} +1 -0
  93. package/lib/buildAccount.js +197 -0
  94. package/lib/commonOpts.js +1 -1
  95. package/lib/constants.js +12 -1
  96. package/lib/developerTestAccounts.js +52 -4
  97. package/lib/errorHandlers/apiErrors.js +1 -1
  98. package/lib/errorHandlers/overrideErrors.js +1 -1
  99. package/lib/errorHandlers/standardErrors.js +1 -1
  100. package/lib/generate-selectors.js +1 -1
  101. package/lib/localDev.js +102 -52
  102. package/lib/marketplace-validate.js +11 -3
  103. package/lib/polling.js +31 -0
  104. package/lib/process.js +1 -1
  105. package/lib/projectStructure.js +5 -2
  106. package/lib/projects.js +68 -15
  107. package/lib/projectsWatch.js +1 -1
  108. package/lib/prompts/accountNamePrompt.js +81 -0
  109. package/lib/prompts/accountsPrompt.js +1 -1
  110. package/lib/prompts/activeInstallConfirmationPrompt.js +20 -0
  111. package/lib/prompts/buildIdPrompt.js +8 -17
  112. package/lib/prompts/cleanUploadPrompt.js +1 -1
  113. package/lib/prompts/cloneAppLocationPrompt.js +32 -0
  114. package/lib/prompts/cmsFieldPrompt.js +1 -1
  115. package/lib/prompts/createApiSamplePrompt.js +1 -1
  116. package/lib/prompts/createFunctionPrompt.js +1 -1
  117. package/lib/prompts/createModulePrompt.js +1 -1
  118. package/lib/prompts/createProjectPrompt.js +32 -10
  119. package/lib/prompts/createTemplatePrompt.js +1 -1
  120. package/lib/prompts/downloadProjectPrompt.js +5 -6
  121. package/lib/prompts/feedbackPrompt.js +1 -1
  122. package/lib/prompts/folderOverwritePrompt.js +1 -1
  123. package/lib/prompts/installPublicAppPrompt.js +51 -0
  124. package/lib/prompts/personalAccessKeyPrompt.js +3 -3
  125. package/lib/prompts/previewPrompt.js +19 -1
  126. package/lib/prompts/projectAddPrompt.js +1 -1
  127. package/lib/prompts/projectDevTargetAccountPrompt.js +48 -6
  128. package/lib/prompts/projectNamePrompt.js +2 -2
  129. package/lib/prompts/projectsLogsPrompt.js +1 -1
  130. package/lib/prompts/sandboxesPrompt.js +13 -42
  131. package/lib/prompts/secretPrompt.js +1 -1
  132. package/lib/prompts/selectPublicAppPrompt.js +84 -0
  133. package/lib/prompts/setAsDefaultAccountPrompt.js +1 -1
  134. package/lib/prompts/uploadPrompt.js +1 -1
  135. package/lib/sandboxSync.js +1 -1
  136. package/lib/sandboxes.js +167 -14
  137. package/lib/serverlessLogs.js +2 -2
  138. package/lib/ui/git.js +1 -1
  139. package/lib/ui/index.js +5 -22
  140. package/lib/ui/serverlessFunctionLogs.js +1 -1
  141. package/package.json +7 -6
  142. package/lib/developerTestAccountCreate.js +0 -186
  143. package/lib/prompts/developerTestAccountNamePrompt.js +0 -29
  144. package/lib/prompts/enterAccountNamePrompt.js +0 -33
  145. package/lib/sandboxCreate.js +0 -319
  146. /package/lib/__tests__/{validation.js → validation.test.js} +0 -0
@@ -13,7 +13,7 @@ const {
13
13
  fetchDeveloperTestAccounts,
14
14
  } = require('@hubspot/local-dev-lib/developerTestAccounts');
15
15
 
16
- const i18nKey = 'cli.lib.prompts.projectDevTargetAccountPrompt';
16
+ const i18nKey = 'lib.prompts.projectDevTargetAccountPrompt';
17
17
 
18
18
  const mapNestedAccount = accountConfig => ({
19
19
  name: uiAccountDescription(accountConfig.portalId, false),
@@ -24,6 +24,12 @@ const mapNestedAccount = accountConfig => ({
24
24
  },
25
25
  });
26
26
 
27
+ const getNonConfigDeveloperTestAccountName = account => {
28
+ return `${account.accountName} [${
29
+ HUBSPOT_ACCOUNT_TYPE_STRINGS[HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST]
30
+ }] (${account.id})`;
31
+ };
32
+
27
33
  const selectSandboxTargetAccountPrompt = async (
28
34
  accounts,
29
35
  defaultAccountConfig
@@ -94,9 +100,11 @@ const selectDeveloperTestTargetAccountPrompt = async (
94
100
  ) => {
95
101
  const defaultAccountId = getAccountId(defaultAccountConfig.name);
96
102
  let choices = [];
97
- let devTestAccountUsage = undefined;
103
+ let devTestAccountsResponse = undefined;
98
104
  try {
99
- devTestAccountUsage = await fetchDeveloperTestAccounts(defaultAccountId);
105
+ devTestAccountsResponse = await fetchDeveloperTestAccounts(
106
+ defaultAccountId
107
+ );
100
108
  } catch (err) {
101
109
  logger.debug('Unable to fetch developer test account usage limits: ', err);
102
110
  }
@@ -110,17 +118,37 @@ const selectDeveloperTestTargetAccountPrompt = async (
110
118
  );
111
119
  let disabledMessage = false;
112
120
  if (
113
- devTestAccountUsage &&
114
- devTestAccountUsage.results.length >= devTestAccountUsage.maxTestPortals
121
+ devTestAccountsResponse &&
122
+ devTestAccountsResponse.results.length >=
123
+ devTestAccountsResponse.maxTestPortals
115
124
  ) {
116
125
  disabledMessage = i18n(`${i18nKey}.developerTestAccountLimit`, {
117
126
  authCommand: uiCommandReference('hs auth'),
118
- limit: devTestAccountUsage.maxTestPortals,
127
+ limit: devTestAccountsResponse.maxTestPortals,
128
+ });
129
+ }
130
+
131
+ let devTestAccountsNotInConfig = [];
132
+ if (devTestAccountsResponse && devTestAccountsResponse.results) {
133
+ const inConfigIds = devTestAccounts.map(d => d.portalId);
134
+ devTestAccountsResponse.results.forEach(acct => {
135
+ if (inConfigIds.indexOf(acct.id) < 0) {
136
+ devTestAccountsNotInConfig.push({
137
+ name: getNonConfigDeveloperTestAccountName(acct),
138
+ value: {
139
+ targetAccountId: acct.id,
140
+ createdNestedAccount: false,
141
+ parentAccountId: defaultAccountId,
142
+ notInConfigAccount: acct,
143
+ },
144
+ });
145
+ }
119
146
  });
120
147
  }
121
148
 
122
149
  choices = [
123
150
  ...devTestAccounts.map(mapNestedAccount),
151
+ ...devTestAccountsNotInConfig,
124
152
  {
125
153
  name: i18n(`${i18nKey}.createNewDeveloperTestAccountOption`),
126
154
  value: {
@@ -172,8 +200,22 @@ const confirmDefaultAccountPrompt = async (accountName, accountType) => {
172
200
  return useDefaultAccount;
173
201
  };
174
202
 
203
+ const confirmUseExistingDeveloperTestAccountPrompt = async account => {
204
+ const { confirmUseExistingDeveloperTestAccount } = await promptUser([
205
+ {
206
+ name: 'confirmUseExistingDeveloperTestAccount',
207
+ type: 'confirm',
208
+ message: i18n(`${i18nKey}.confirmUseExistingDeveloperTestAccount`, {
209
+ accountName: getNonConfigDeveloperTestAccountName(account),
210
+ }),
211
+ },
212
+ ]);
213
+ return confirmUseExistingDeveloperTestAccount;
214
+ };
215
+
175
216
  module.exports = {
176
217
  selectSandboxTargetAccountPrompt,
177
218
  selectDeveloperTestTargetAccountPrompt,
178
219
  confirmDefaultAccountPrompt,
220
+ confirmUseExistingDeveloperTestAccountPrompt,
179
221
  };
@@ -3,7 +3,7 @@ const { i18n } = require('../lang');
3
3
  const { ensureProjectExists } = require('../projects');
4
4
  const { uiAccountDescription } = require('../ui');
5
5
 
6
- const i18nKey = 'cli.lib.prompts.projectNamePrompt';
6
+ const i18nKey = 'lib.prompts.projectNamePrompt';
7
7
 
8
8
  const projectNamePrompt = (accountId, options = {}) => {
9
9
  return promptUser({
@@ -14,7 +14,7 @@ const projectNamePrompt = (accountId, options = {}) => {
14
14
  if (typeof val !== 'string' || !val) {
15
15
  return i18n(`${i18nKey}.errors.invalidName`);
16
16
  }
17
- const projectExists = await ensureProjectExists(accountId, val, {
17
+ const { projectExists } = await ensureProjectExists(accountId, val, {
18
18
  allowCreate: false,
19
19
  noLogs: true,
20
20
  });
@@ -10,7 +10,7 @@ const {
10
10
  const { logger } = require('@hubspot/local-dev-lib/logger');
11
11
  const { EXIT_CODES } = require('../enums/exitCodes');
12
12
 
13
- const i18nKey = 'cli.lib.prompts.projectLogsPrompt';
13
+ const i18nKey = 'lib.prompts.projectLogsPrompt';
14
14
 
15
15
  const SERVERLESS_FUNCTION_TYPES = {
16
16
  APP_FUNCTION: 'app-function',
@@ -1,13 +1,12 @@
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,
7
6
  } = require('@hubspot/local-dev-lib/constants/config');
8
7
  const { isSandbox } = require('../accountTypes');
9
8
 
10
- const i18nKey = 'cli.lib.prompts.sandboxesPrompt';
9
+ const i18nKey = 'lib.prompts.sandboxesPrompt';
11
10
 
12
11
  const mapSandboxAccountChoices = portals =>
13
12
  portals
@@ -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
  };
@@ -1,7 +1,7 @@
1
1
  const { promptUser } = require('./promptUtils');
2
2
  const { i18n } = require('../lang');
3
3
 
4
- const i18nKey = 'cli.lib.prompts.secretPrompt';
4
+ const i18nKey = 'lib.prompts.secretPrompt';
5
5
 
6
6
  const SECRET_VALUE_PROMPT = {
7
7
  name: 'secretValue',
@@ -0,0 +1,84 @@
1
+ const { promptUser } = require('./promptUtils');
2
+ const { i18n } = require('../lang');
3
+ const { uiLine } = require('../ui');
4
+ const { logApiErrorInstance } = require('../errorHandlers/apiErrors');
5
+ const { logger } = require('@hubspot/local-dev-lib/logger');
6
+ const {
7
+ fetchPublicAppsForPortal,
8
+ } = require('@hubspot/local-dev-lib/api/appsDev');
9
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
10
+
11
+ const i18nKey = 'lib.prompts.selectPublicAppPrompt';
12
+
13
+ const fetchPublicAppOptions = async (accountId, accountName, migrateApp) => {
14
+ try {
15
+ const publicApps = await fetchPublicAppsForPortal(accountId);
16
+ const filteredPublicApps = publicApps.filter(
17
+ app => !app.projectId && !app.sourceId
18
+ );
19
+
20
+ if (
21
+ !filteredPublicApps.length ||
22
+ (migrateApp &&
23
+ !filteredPublicApps.find(app => !app.preventProjectMigrations))
24
+ ) {
25
+ const headerTranslationKey = migrateApp
26
+ ? 'noAppsMigration'
27
+ : 'noAppsClone';
28
+ const messageTranslationKey = migrateApp
29
+ ? 'noAppsMigrationMessage'
30
+ : 'noAppsCloneMessage';
31
+ uiLine();
32
+ logger.error(i18n(`${i18nKey}.errors.${headerTranslationKey}`));
33
+ logger.log(
34
+ i18n(`${i18nKey}.errors.${messageTranslationKey}`, { accountName })
35
+ );
36
+ uiLine();
37
+ process.exit(EXIT_CODES.SUCCESS);
38
+ }
39
+ return filteredPublicApps;
40
+ } catch (error) {
41
+ logApiErrorInstance(error, { accountId });
42
+ logger.error(i18n(`${i18nKey}.errors.errorFetchingApps`));
43
+ process.exit(EXIT_CODES.ERROR);
44
+ }
45
+ };
46
+
47
+ const selectPublicAppPrompt = async ({
48
+ accountId,
49
+ accountName,
50
+ migrateApp = false,
51
+ }) => {
52
+ const publicApps = await fetchPublicAppOptions(
53
+ accountId,
54
+ accountName,
55
+ (migrateApp = false)
56
+ );
57
+ const translationKey = migrateApp ? 'selectAppIdMigrate' : 'selectAppIdClone';
58
+
59
+ return promptUser([
60
+ {
61
+ name: 'appId',
62
+ message: i18n(`${i18nKey}.${translationKey}`, {
63
+ accountName,
64
+ }),
65
+ type: 'list',
66
+ choices: publicApps.map(app => {
67
+ if (migrateApp && app.preventProjectMigrations) {
68
+ return {
69
+ name: `${app.name} (${app.id})`,
70
+ disabled: i18n(`${i18nKey}.errors.cannotBeMigrated`),
71
+ };
72
+ }
73
+ return {
74
+ name: `${app.name} (${app.id})`,
75
+ value: app.id,
76
+ };
77
+ }),
78
+ },
79
+ ]);
80
+ };
81
+
82
+ module.exports = {
83
+ selectPublicAppPrompt,
84
+ };
@@ -5,7 +5,7 @@ const {
5
5
  const { promptUser } = require('./promptUtils');
6
6
  const { i18n } = require('../lang');
7
7
 
8
- const i18nKey = 'cli.lib.prompts.setAsDefaultAccountPrompt';
8
+ const i18nKey = 'lib.prompts.setAsDefaultAccountPrompt';
9
9
 
10
10
  const setAsDefaultAccountPrompt = async accountName => {
11
11
  const config = getConfig();
@@ -3,7 +3,7 @@ const { getCwd } = require('@hubspot/local-dev-lib/path');
3
3
  const { promptUser } = require('./promptUtils');
4
4
  const { i18n } = require('../lang');
5
5
 
6
- const i18nKey = 'cli.lib.prompts.uploadPrompt';
6
+ const i18nKey = 'lib.prompts.uploadPrompt';
7
7
 
8
8
  const uploadPrompt = (promptOptions = {}) => {
9
9
  return promptUser([
@@ -21,7 +21,7 @@ const { getSandboxTypeAsString } = require('./sandboxes');
21
21
  const { getAccountId } = require('@hubspot/local-dev-lib/config');
22
22
  const { uiAccountDescription } = require('./ui');
23
23
 
24
- const i18nKey = 'cli.lib.sandbox.sync';
24
+ const i18nKey = 'lib.sandbox.sync';
25
25
 
26
26
  /**
27
27
  * @param {Object} accountConfig - Account config of sandbox portal
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',
@@ -39,9 +50,6 @@ const getSandboxTypeAsString = accountType => {
39
50
  return 'standard';
40
51
  };
41
52
 
42
- const getSandboxName = config =>
43
- `[${getSandboxTypeAsString(config.accountType)} sandbox] `;
44
-
45
53
  function getHasSandboxesByType(parentAccountConfig, type) {
46
54
  const config = getConfig();
47
55
  const parentPortalId = getAccountId(parentAccountConfig.portalId);
@@ -103,9 +111,7 @@ const getSyncTypesWithContactRecordsPrompt = async (
103
111
  {
104
112
  name: 'contactRecordsSyncPrompt',
105
113
  type: 'confirm',
106
- message: i18n(
107
- `cli.lib.sandbox.sync.confirm.syncContactRecords.${langKey}`
108
- ),
114
+ message: i18n(`lib.sandbox.sync.confirm.syncContactRecords.${langKey}`),
109
115
  },
110
116
  ]);
111
117
  if (!contactRecordsSyncPrompt) {
@@ -138,7 +144,7 @@ const validateSandboxUsageLimits = async (accountConfig, sandboxType, env) => {
138
144
  if (hasDevelopmentSandboxes) {
139
145
  throw new Error(
140
146
  i18n(
141
- `cli.lib.sandbox.create.failure.alreadyInConfig.developer.${
147
+ `lib.sandbox.create.failure.alreadyInConfig.developer.${
142
148
  plural ? 'other' : 'one'
143
149
  }`,
144
150
  {
@@ -151,7 +157,7 @@ const validateSandboxUsageLimits = async (accountConfig, sandboxType, env) => {
151
157
  const baseUrl = getHubSpotWebsiteOrigin(env);
152
158
  throw new Error(
153
159
  i18n(
154
- `cli.lib.sandbox.create.failure.limit.developer.${
160
+ `lib.sandbox.create.failure.limit.developer.${
155
161
  plural ? 'other' : 'one'
156
162
  }`,
157
163
  {
@@ -175,7 +181,7 @@ const validateSandboxUsageLimits = async (accountConfig, sandboxType, env) => {
175
181
  if (hasStandardSandboxes) {
176
182
  throw new Error(
177
183
  i18n(
178
- `cli.lib.sandbox.create.failure.alreadyInConfig.standard.${
184
+ `lib.sandbox.create.failure.alreadyInConfig.standard.${
179
185
  plural ? 'other' : 'one'
180
186
  }`,
181
187
  {
@@ -188,7 +194,7 @@ const validateSandboxUsageLimits = async (accountConfig, sandboxType, env) => {
188
194
  const baseUrl = getHubSpotWebsiteOrigin(env);
189
195
  throw new Error(
190
196
  i18n(
191
- `cli.lib.sandbox.create.failure.limit.standard.${
197
+ `lib.sandbox.create.failure.limit.standard.${
192
198
  plural ? 'other' : 'one'
193
199
  }`,
194
200
  {
@@ -203,6 +209,153 @@ const validateSandboxUsageLimits = async (accountConfig, sandboxType, env) => {
203
209
  }
204
210
  };
205
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
+
206
359
  const ACTIVE_TASK_POLL_INTERVAL = 1000;
207
360
 
208
361
  const isTaskComplete = task => {
@@ -229,7 +382,7 @@ function pollSyncTaskStatus(
229
382
  syncStatusUrl,
230
383
  allowEarlyTermination = true
231
384
  ) {
232
- const i18nKey = 'cli.lib.sandbox.sync.types';
385
+ const i18nKey = 'lib.sandbox.sync.types';
233
386
  const progressBar = CliProgressMultibarManager.init();
234
387
  const mergeTasks = {
235
388
  'lead-flows': 'forms', // lead-flows are a subset of forms. We combine these in the UI as a single item, so we want to merge here for consistency.
@@ -244,7 +397,7 @@ function pollSyncTaskStatus(
244
397
  logger.log('Exiting, sync will continue in the background.');
245
398
  logger.log('');
246
399
  logger.log(
247
- i18n('cli.lib.sandbox.sync.info.syncStatus', {
400
+ i18n('lib.sandbox.sync.info.syncStatus', {
248
401
  url: syncStatusUrl,
249
402
  })
250
403
  );
@@ -344,7 +497,6 @@ module.exports = {
344
497
  sandboxTypeMap,
345
498
  sandboxApiTypeMap,
346
499
  syncTypes,
347
- getSandboxName,
348
500
  getSandboxTypeAsString,
349
501
  getHasSandboxesByType,
350
502
  getSandboxLimit,
@@ -352,4 +504,5 @@ module.exports = {
352
504
  getAvailableSyncTypes,
353
505
  getSyncTypesWithContactRecordsPrompt,
354
506
  pollSyncTaskStatus,
507
+ handleSandboxCreateError,
355
508
  };
@@ -52,7 +52,7 @@ const tailLogs = async ({
52
52
  initialAfter = latestLog && base64EncodeString(latestLog.id);
53
53
  } catch (e) {
54
54
  // A 404 means no latest log exists(never executed)
55
- if (e.statusCode !== 404) {
55
+ if (e.response && e.response.status !== 404) {
56
56
  await logServerlessFunctionApiErrorInstance(
57
57
  accountId,
58
58
  e,
@@ -68,7 +68,7 @@ const tailLogs = async ({
68
68
  latestLog = await tailCall(after);
69
69
  nextAfter = latestLog.paging.next.after;
70
70
  } catch (e) {
71
- if (e.statusCode !== 404) {
71
+ if (e.response && e.response.status !== 404) {
72
72
  logApiErrorInstance(
73
73
  e,
74
74
  new ApiErrorContext({
package/lib/ui/git.js CHANGED
@@ -3,7 +3,7 @@ const { checkGitInclusion } = require('@hubspot/local-dev-lib/gitignore');
3
3
  const { logger } = require('@hubspot/local-dev-lib/logger');
4
4
  const { i18n } = require('../lang');
5
5
 
6
- const i18nKey = 'cli.lib.ui.git';
6
+ const i18nKey = 'lib.ui.git';
7
7
 
8
8
  function checkAndWarnGitInclusion(configPath) {
9
9
  try {