@hubspot/cli 5.2.0 → 5.2.1-beta.1
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/bin/cli.js +6 -1
- package/commands/accounts/clean.js +3 -3
- package/commands/accounts/list.js +17 -5
- package/commands/project/dev.js +92 -260
- package/commands/project/upload.js +4 -1
- package/commands/sandbox/sync.js +5 -3
- package/lang/en.lyaml +51 -15
- package/lib/LocalDevManager.js +31 -33
- package/lib/accountTypes.js +34 -0
- package/lib/developerTestAccountCreate.js +186 -0
- package/lib/developerTestAccounts.js +50 -4
- package/lib/errorHandlers/apiErrors.js +6 -221
- package/lib/errorHandlers/fileSystemErrors.js +5 -40
- package/lib/errorHandlers/standardErrors.js +3 -9
- package/lib/localDev.js +401 -0
- package/lib/projectStructure.js +7 -0
- package/lib/projects.js +40 -2
- package/lib/prompts/developerTestAccountNamePrompt.js +29 -0
- package/lib/prompts/projectDevTargetAccountPrompt.js +84 -13
- package/lib/prompts/sandboxesPrompt.js +1 -1
- package/lib/sandboxSync.js +1 -1
- package/lib/sandboxes.js +1 -16
- package/lib/ui/index.js +10 -2
- package/package.json +5 -5
package/lang/en.lyaml
CHANGED
|
@@ -475,21 +475,10 @@ en:
|
|
|
475
475
|
describe: "Start local dev for the current project"
|
|
476
476
|
logs:
|
|
477
477
|
betaMessage: "HubSpot projects local development"
|
|
478
|
-
nonSandboxWarning: "Testing in a sandbox is strongly recommended. To switch the target account, select an option below or run {{#bold}}`hs accounts use`{{/bold}} before running the command again."
|
|
479
478
|
placeholderAccountSelection: "Using default account as target account (for now)"
|
|
480
|
-
projectMustExistExplanation: "The project {{ projectName }} does not exist in the target account {{ accountIdentifier}}. This command requires the project to exist in the target account."
|
|
481
|
-
choseNotToCreateProject: "Exiting because this command requires the project to exist in the target account."
|
|
482
|
-
initialUploadMessage: "HubSpot Local Dev Server Startup"
|
|
483
|
-
declineDefaultAccountExplanation: "To develop on a different account, run {{ useCommand }} to change your default account, then re-run {{ devCommand }}."
|
|
484
|
-
status:
|
|
485
|
-
creatingProject: "Creating project {{ projectName }} in {{ accountIdentifier }}"
|
|
486
|
-
createdProject: "Created project {{ projectName }} in {{ accountIdentifier }}"
|
|
487
|
-
failedToCreateProject: "Failed to create project in the target account."
|
|
488
|
-
prompt:
|
|
489
|
-
createProject: "Create new project {{ projectName}} in {{#bold}}[{{ accountIdentifier }}]{{/bold}}?"
|
|
490
479
|
errors:
|
|
491
480
|
noProjectConfig: "No project detected. Please run this command again from a project directory."
|
|
492
|
-
|
|
481
|
+
invalidProjectComponents: "Projects cannot contain both private and public apps. Move your apps to separate projects before attempting local development."
|
|
493
482
|
examples:
|
|
494
483
|
default: "Start local dev for the current project"
|
|
495
484
|
create:
|
|
@@ -952,6 +941,25 @@ en:
|
|
|
952
941
|
setupError: "Failed to setup local dev server: {{ message }}"
|
|
953
942
|
startError: "Failed to start local dev server: {{ message }}"
|
|
954
943
|
fileChangeError: "Failed to notify local dev server of file change: {{ message }}"
|
|
944
|
+
localDev:
|
|
945
|
+
confirmDefaultAccountIsTarget:
|
|
946
|
+
declineDefaultAccountExplanation: "To develop on a different account, run {{ useCommand }} to change your default account, then re-run {{ devCommand }}."
|
|
947
|
+
checkCorrectParentAccountType:
|
|
948
|
+
standardAccountNotSupported: "This project contains a public app. Local development of public apps is only supported on developer accounts and developer test accounts. Change your default account using {{#bold}}`hs accounts use`{{/bold}}, or link a new account with {{#bold}}`hs auth`{{/bold}}."
|
|
949
|
+
suggestRecommendedNestedAccount:
|
|
950
|
+
nonSandboxWarning: "Testing in a sandbox is strongly recommended. To switch the target account, select an option below or run {{#bold}}`hs accounts use`{{/bold}} before running the command again."
|
|
951
|
+
publicAppNonDeveloperTestAccountWarning: "Local development of public apps is only supported in {{#bold}}developer test accounts{{/bold}}."
|
|
952
|
+
privateAppNonDeveloperTestAccountWarning: "Local development of private apps is only supported in {{#bold}}developer test accounts{{/bold}}"
|
|
953
|
+
createNewProjectForLocalDev:
|
|
954
|
+
projectMustExistExplanation: "The project {{ projectName }} does not exist in the target account {{ accountIdentifier}}. This command requires the project to exist in the target account."
|
|
955
|
+
createProject: "Create new project {{ projectName}} in {{#bold}}[{{ accountIdentifier }}]{{/bold}}?"
|
|
956
|
+
choseNotToCreateProject: "Exiting because this command requires the project to exist in the target account."
|
|
957
|
+
creatingProject: "Creating project {{ projectName }} in {{ accountIdentifier }}"
|
|
958
|
+
createdProject: "Created project {{ projectName }} in {{ accountIdentifier }}"
|
|
959
|
+
failedToCreateProject: "Failed to create project in the target account."
|
|
960
|
+
createInitialBuildForNewProject:
|
|
961
|
+
initialUploadMessage: "HubSpot Local Dev Server Startup"
|
|
962
|
+
projectLockedError: "Your project is locked. This may mean that another user is running the {{#bold}}`hs project watch`{{/bold}} command for this project. If this is you, unlock the project in Projects UI."
|
|
955
963
|
projects:
|
|
956
964
|
config:
|
|
957
965
|
srcOutsideProjectDir: "Invalid value for 'srcDir' in {{ projectConfig }}: {{#bold}}srcDir: \"{{ srcDir }}\"{{/bold}}\n\t'srcDir' must be a relative path to a folder under the project root, such as \".\" or \"./src\""
|
|
@@ -1065,10 +1073,12 @@ en:
|
|
|
1065
1073
|
prompts:
|
|
1066
1074
|
projectDevTargetAccountPrompt:
|
|
1067
1075
|
createNewSandboxOption: "<Test on a new development sandbox>"
|
|
1076
|
+
createNewDeveloperTestAccountOption: "<Test on a new developer test account>"
|
|
1068
1077
|
chooseDefaultAccountOption: "<{{#bold}}\U00002757{{/bold}} Test on this production account {{#bold}}\U00002757{{/bold}}>"
|
|
1069
|
-
promptMessage: "[--account] Choose a
|
|
1078
|
+
promptMessage: "[--account] Choose a {{ accountType }} under {{ accountIdentifier }} to test with:"
|
|
1070
1079
|
sandboxLimit: "Your account reached the limit of {{ limit }} development sandboxes"
|
|
1071
1080
|
sandboxLimitWithSuggestion: "Your account reached the limit of {{ limit }} development sandboxes. Run {{ authCommand }} to add an existing one to the config."
|
|
1081
|
+
developerTestAccountLimit: "Your account reached the limit of {{ limit }} developer test accounts."
|
|
1072
1082
|
confirmDefaultAccount: "Continue testing on {{#bold}}{{ accountName }} ({{ accountType }}){{/bold}}? (Y/n)"
|
|
1073
1083
|
projectLogsPrompt:
|
|
1074
1084
|
projectName:
|
|
@@ -1150,6 +1160,13 @@ en:
|
|
|
1150
1160
|
invalidTemplate: "[--template] Could not find template {{ template }}. Please choose an available template."
|
|
1151
1161
|
noProjectsInConfig: "Please ensure that there is a config.json file that contains a \"projects\" field."
|
|
1152
1162
|
missingPropertiesInConfig: "Please ensure that each of the projects in your config.json file contain the following properties: [\"name\", \"label\", \"path\", \"insertPath\"]."
|
|
1163
|
+
developerTestAccountPrompt:
|
|
1164
|
+
name:
|
|
1165
|
+
message: "Name your developer test account"
|
|
1166
|
+
errors:
|
|
1167
|
+
invalidName: "You entered an invalid name. Please try again."
|
|
1168
|
+
nameRequired: "The name may not be blank. Please try again."
|
|
1169
|
+
accountNameExists: "Account with name \"{{ name }}\" already exists in the CLI config, please enter a different name."
|
|
1153
1170
|
downloadProjectPrompt:
|
|
1154
1171
|
selectProject: "Select a project to download:"
|
|
1155
1172
|
errors:
|
|
@@ -1217,6 +1234,25 @@ en:
|
|
|
1217
1234
|
options:
|
|
1218
1235
|
options:
|
|
1219
1236
|
describe: "Options to pass to javascript fields files"
|
|
1237
|
+
developerTestAccount:
|
|
1238
|
+
create:
|
|
1239
|
+
loading:
|
|
1240
|
+
add: "Creating developer test account {{#bold}}{{ accountName }}{{/bold}}"
|
|
1241
|
+
fail: "Failed to create a developer test account {{#bold}}{{ accountName }}{{/bold}}."
|
|
1242
|
+
succeed: "Successfully created a developer test account {{#bold}}{{ name }}{{/bold}} with portalId {{#bold}}{{ accountId }}{{/bold}}."
|
|
1243
|
+
success:
|
|
1244
|
+
configFileUpdated: "{{ configFilename }} updated with {{ authMethod }} for account {{ account }}."
|
|
1245
|
+
failure:
|
|
1246
|
+
invalidUser: "Couldn't create {{#bold}}{{ accountName }}{{/bold}} because your account has been removed from {{#bold}}{{ parentAccountName }}{{/bold}} or your permission set doesn't allow you to create the sandbox. To update your permissions, contact a super admin in {{#bold}}{{ parentAccountName }}{{/bold}}."
|
|
1247
|
+
limit: "{{#bold}}{{ accountName }}{{/bold}} reached the limit of {{ limit }} developer test accounts.
|
|
1248
|
+
\n- To connect a developer test account to your HubSpot CLI, run {{#bold}}hs auth{{/bold}} and follow the prompts."
|
|
1249
|
+
alreadyInConfig: "{{#bold}}{{ accountName }}{{/bold}} reached the limit of {{ limit }} developer test accounts.
|
|
1250
|
+
\n- To use an existing developer test account, run {{#bold}}hs accounts use{{/bold}}."
|
|
1251
|
+
scopes:
|
|
1252
|
+
message: "The personal access key you provided doesn't include developer test account permissions."
|
|
1253
|
+
instructions: "To update CLI permissions for \"{{ accountName }}\":
|
|
1254
|
+
\n- Go to {{ url }}, deactivate the existing personal access key, and create a new one that includes developer test account permissions.
|
|
1255
|
+
\n- Update the CLI config for this account by running {{#bold}}hs auth{{/bold}} and entering the new key.\n"
|
|
1220
1256
|
sandbox:
|
|
1221
1257
|
create:
|
|
1222
1258
|
loading:
|
|
@@ -1232,7 +1268,7 @@ en:
|
|
|
1232
1268
|
configFileUpdated: "{{ configFilename }} updated with {{ authMethod }} for account {{ account }}."
|
|
1233
1269
|
failure:
|
|
1234
1270
|
invalidUser: "Couldn't create {{#bold}}{{ accountName }}{{/bold}} because your account has been removed from {{#bold}}{{ parentAccountName }}{{/bold}} or your permission set doesn't allow you to create the sandbox. To update your permissions, contact a super admin in {{#bold}}{{ parentAccountName }}{{/bold}}."
|
|
1235
|
-
403Gating: "Couldn't create {{#bold}}{{ accountName }}{{/bold}} because {{#bold}}{{ parentAccountName }}{{/bold}} does not have access to development sandboxes. To opt in to the CRM Development Beta and use development sandboxes, visit https://app.hubspot.com/l/
|
|
1271
|
+
403Gating: "Couldn't create {{#bold}}{{ accountName }}{{/bold}} because {{#bold}}{{ parentAccountName }}{{/bold}} does not have access to development sandboxes. To opt in to the CRM Development Beta and use development sandboxes, visit https://app.hubspot.com/l/product-updates/in-beta?update=13899236."
|
|
1236
1272
|
limit:
|
|
1237
1273
|
developer:
|
|
1238
1274
|
one: "{{#bold}}{{ accountName }}{{/bold}} reached the limit of {{ limit }} development sandbox.
|
|
@@ -1336,7 +1372,7 @@ en:
|
|
|
1336
1372
|
400: "The {{ messageDetail }} was bad."
|
|
1337
1373
|
401: "The {{ messageDetail }} was unauthorized."
|
|
1338
1374
|
403MissingScope: "Couldn't run the project command because there are scopes missing in your production account. To update scopes, deactivate your current personal access key for {{ accountId }}, and generate a new one. Then run `hs auth` to update the CLI with the new key."
|
|
1339
|
-
403Gating: "The current target account {{ accountId }} does not have access to HubSpot projects. To opt in to the CRM Development Beta and use projects, visit https://app.hubspot.com/l/
|
|
1375
|
+
403Gating: "The current target account {{ accountId }} does not have access to HubSpot projects. To opt in to the CRM Development Beta and use projects, visit https://app.hubspot.com/l/product-updates/in-beta?update=13899236."
|
|
1340
1376
|
403: "The {{ messageDetail }} was forbidden."
|
|
1341
1377
|
404Request: 'The {{ action }} failed because "{{ request }}" was not found in account {{ accountId }}.'
|
|
1342
1378
|
404: "The {{ messageDetail }} was not found."
|
package/lib/LocalDevManager.js
CHANGED
|
@@ -16,7 +16,6 @@ const { getProjectDetailUrl } = require('./projects');
|
|
|
16
16
|
const {
|
|
17
17
|
CONFIG_FILES,
|
|
18
18
|
COMPONENT_TYPES,
|
|
19
|
-
findProjectComponents,
|
|
20
19
|
getAppCardConfigs,
|
|
21
20
|
} = require('./projectStructure');
|
|
22
21
|
const {
|
|
@@ -47,6 +46,7 @@ class LocalDevManager {
|
|
|
47
46
|
this.isGithubLinked = options.isGithubLinked;
|
|
48
47
|
this.watcher = null;
|
|
49
48
|
this.uploadWarnings = {};
|
|
49
|
+
this.runnableComponents = this.getRunnableComponents(options.components);
|
|
50
50
|
|
|
51
51
|
this.projectSourceDir = path.join(
|
|
52
52
|
this.projectDir,
|
|
@@ -57,6 +57,27 @@ class LocalDevManager {
|
|
|
57
57
|
logger.log(i18n(`${i18nKey}.failedToInitialize`));
|
|
58
58
|
process.exit(EXIT_CODES.ERROR);
|
|
59
59
|
}
|
|
60
|
+
|
|
61
|
+
// The project is empty, there is nothing to run locally
|
|
62
|
+
if (!options.components.length) {
|
|
63
|
+
logger.error(i18n(`${i18nKey}.noComponents`));
|
|
64
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The project does not contain any components that support local development
|
|
68
|
+
if (!this.runnableComponents.length) {
|
|
69
|
+
logger.error(
|
|
70
|
+
i18n(`${i18nKey}.noRunnableComponents`, {
|
|
71
|
+
projectSourceDir: this.projectSourceDir,
|
|
72
|
+
command: uiCommandReference('hs project add'),
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getRunnableComponents(components) {
|
|
80
|
+
return components.filter(component => component.runnable);
|
|
60
81
|
}
|
|
61
82
|
|
|
62
83
|
async start() {
|
|
@@ -75,30 +96,7 @@ class LocalDevManager {
|
|
|
75
96
|
process.exit(EXIT_CODES.SUCCESS);
|
|
76
97
|
}
|
|
77
98
|
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
// The project is empty, there is nothing to run locally
|
|
81
|
-
if (!components.length) {
|
|
82
|
-
logger.error(i18n(`${i18nKey}.noComponents`));
|
|
83
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const runnableComponents = components.filter(
|
|
87
|
-
component => component.runnable
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
// The project does not contain any components that support local development
|
|
91
|
-
if (!runnableComponents.length) {
|
|
92
|
-
logger.error(
|
|
93
|
-
i18n(`${i18nKey}.noRunnableComponents`, {
|
|
94
|
-
projectSourceDir: this.projectSourceDir,
|
|
95
|
-
command: uiCommandReference('hs project add'),
|
|
96
|
-
})
|
|
97
|
-
);
|
|
98
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const setupSucceeded = await this.devServerSetup(runnableComponents);
|
|
99
|
+
const setupSucceeded = await this.devServerSetup();
|
|
102
100
|
|
|
103
101
|
if (!setupSucceeded) {
|
|
104
102
|
process.exit(EXIT_CODES.ERROR);
|
|
@@ -130,7 +128,7 @@ class LocalDevManager {
|
|
|
130
128
|
await this.devServerStart();
|
|
131
129
|
|
|
132
130
|
// Initialize project file watcher to detect configuration file changes
|
|
133
|
-
this.startWatching(
|
|
131
|
+
this.startWatching();
|
|
134
132
|
|
|
135
133
|
this.updateKeypressListeners();
|
|
136
134
|
|
|
@@ -138,7 +136,7 @@ class LocalDevManager {
|
|
|
138
136
|
|
|
139
137
|
// Verify that there are no mismatches between components in the local project
|
|
140
138
|
// and components in the deployed build of the project.
|
|
141
|
-
this.compareLocalProjectToDeployed(
|
|
139
|
+
this.compareLocalProjectToDeployed();
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
async stop(showProgress = true) {
|
|
@@ -235,14 +233,14 @@ class LocalDevManager {
|
|
|
235
233
|
}.bind(this);
|
|
236
234
|
}
|
|
237
235
|
|
|
238
|
-
compareLocalProjectToDeployed(
|
|
236
|
+
compareLocalProjectToDeployed() {
|
|
239
237
|
const deployedComponentNames = this.deployedBuild.subbuildStatuses.map(
|
|
240
238
|
subbuildStatus => subbuildStatus.buildName
|
|
241
239
|
);
|
|
242
240
|
|
|
243
241
|
let missingComponents = [];
|
|
244
242
|
|
|
245
|
-
runnableComponents.forEach(({ type, config, path }) => {
|
|
243
|
+
this.runnableComponents.forEach(({ type, config, path }) => {
|
|
246
244
|
if (Object.values(COMPONENT_TYPES).includes(type)) {
|
|
247
245
|
const cardConfigs = getAppCardConfigs(config, path);
|
|
248
246
|
|
|
@@ -277,12 +275,12 @@ class LocalDevManager {
|
|
|
277
275
|
}
|
|
278
276
|
}
|
|
279
277
|
|
|
280
|
-
startWatching(
|
|
278
|
+
startWatching() {
|
|
281
279
|
this.watcher = chokidar.watch(this.projectDir, {
|
|
282
280
|
ignoreInitial: true,
|
|
283
281
|
});
|
|
284
282
|
|
|
285
|
-
const configPaths = runnableComponents
|
|
283
|
+
const configPaths = this.runnableComponents
|
|
286
284
|
.filter(({ type }) => Object.values(COMPONENT_TYPES).includes(type))
|
|
287
285
|
.map(component => {
|
|
288
286
|
const appConfigPath = path.join(
|
|
@@ -321,10 +319,10 @@ class LocalDevManager {
|
|
|
321
319
|
}
|
|
322
320
|
}
|
|
323
321
|
|
|
324
|
-
async devServerSetup(
|
|
322
|
+
async devServerSetup() {
|
|
325
323
|
try {
|
|
326
324
|
await DevServerManager.setup({
|
|
327
|
-
components,
|
|
325
|
+
components: this.runnableComponents,
|
|
328
326
|
onUploadRequired: this.logUploadWarning.bind(this),
|
|
329
327
|
accountId: this.targetAccountId,
|
|
330
328
|
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const {
|
|
2
|
+
HUBSPOT_ACCOUNT_TYPES,
|
|
3
|
+
} = require('@hubspot/local-dev-lib/constants/config');
|
|
4
|
+
|
|
5
|
+
const isAccountType = (config, accountType) =>
|
|
6
|
+
config.accountType && config.accountType === accountType;
|
|
7
|
+
|
|
8
|
+
const isStandardAccount = config =>
|
|
9
|
+
isAccountType(config, HUBSPOT_ACCOUNT_TYPES.STANDARD);
|
|
10
|
+
|
|
11
|
+
const isSandbox = config =>
|
|
12
|
+
isAccountType(config, HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX) ||
|
|
13
|
+
isAccountType(config, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX);
|
|
14
|
+
|
|
15
|
+
const isStandardSandbox = config =>
|
|
16
|
+
isAccountType(config, HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX);
|
|
17
|
+
|
|
18
|
+
const isDevelopmentSandbox = config =>
|
|
19
|
+
isAccountType(config, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX);
|
|
20
|
+
|
|
21
|
+
const isDeveloperTestAccount = config =>
|
|
22
|
+
isAccountType(config, HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST);
|
|
23
|
+
|
|
24
|
+
const isAppDeveloperAccount = config =>
|
|
25
|
+
isAccountType(config, HUBSPOT_ACCOUNT_TYPES.APP_DEVELOPER);
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
isStandardAccount,
|
|
29
|
+
isSandbox,
|
|
30
|
+
isStandardSandbox,
|
|
31
|
+
isDevelopmentSandbox,
|
|
32
|
+
isDeveloperTestAccount,
|
|
33
|
+
isAppDeveloperAccount,
|
|
34
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const SpinniesManager = require('./ui/SpinniesManager');
|
|
2
|
+
const {
|
|
3
|
+
getAccountId,
|
|
4
|
+
accountNameExistsInConfig,
|
|
5
|
+
updateAccountConfig,
|
|
6
|
+
writeConfig,
|
|
7
|
+
} = require('@hubspot/local-dev-lib/config');
|
|
8
|
+
const { logger } = require('@hubspot/local-dev-lib/logger');
|
|
9
|
+
const {
|
|
10
|
+
createDeveloperTestAccount,
|
|
11
|
+
} = require('@hubspot/local-dev-lib/developerTestAccounts');
|
|
12
|
+
const { i18n } = require('./lang');
|
|
13
|
+
const {
|
|
14
|
+
debugErrorAndContext,
|
|
15
|
+
logErrorInstance,
|
|
16
|
+
} = require('./errorHandlers/standardErrors');
|
|
17
|
+
const {
|
|
18
|
+
isMissingScopeError,
|
|
19
|
+
isSpecifiedError,
|
|
20
|
+
} = require('@hubspot/local-dev-lib/errors/apiErrors');
|
|
21
|
+
const {
|
|
22
|
+
getAccessToken,
|
|
23
|
+
updateConfigWithAccessToken,
|
|
24
|
+
} = require('@hubspot/local-dev-lib/personalAccessKey');
|
|
25
|
+
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
|
|
26
|
+
const { uiAccountDescription } = require('./ui');
|
|
27
|
+
const {
|
|
28
|
+
personalAccessKeyPrompt,
|
|
29
|
+
} = require('./prompts/personalAccessKeyPrompt');
|
|
30
|
+
const { enterAccountNamePrompt } = require('./prompts/enterAccountNamePrompt');
|
|
31
|
+
|
|
32
|
+
const i18nKey = 'cli.lib.developerTestAccount';
|
|
33
|
+
|
|
34
|
+
const saveDevTestAccountToConfig = async (env, result, force = false) => {
|
|
35
|
+
let personalAccessKey = result.personalAccessKey;
|
|
36
|
+
if (!personalAccessKey) {
|
|
37
|
+
const configData = await personalAccessKeyPrompt({
|
|
38
|
+
env,
|
|
39
|
+
account: result.id,
|
|
40
|
+
});
|
|
41
|
+
personalAccessKey = configData.personalAccessKey;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const token = await getAccessToken(personalAccessKey, env);
|
|
45
|
+
const updatedConfig = await updateConfigWithAccessToken(
|
|
46
|
+
token,
|
|
47
|
+
personalAccessKey,
|
|
48
|
+
env
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
let validName = updatedConfig.name;
|
|
52
|
+
if (!updatedConfig.name) {
|
|
53
|
+
const nameForConfig = result.accountName
|
|
54
|
+
.toLowerCase()
|
|
55
|
+
.split(' ')
|
|
56
|
+
.join('-');
|
|
57
|
+
validName = nameForConfig;
|
|
58
|
+
const invalidAccountName = accountNameExistsInConfig(nameForConfig);
|
|
59
|
+
if (invalidAccountName) {
|
|
60
|
+
if (!force) {
|
|
61
|
+
logger.log('');
|
|
62
|
+
logger.warn(
|
|
63
|
+
i18n(
|
|
64
|
+
`cli.lib.prompts.enterAccountNamePrompt.errors.accountNameExists`,
|
|
65
|
+
{ name: nameForConfig }
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
const { name: promptName } = await enterAccountNamePrompt(
|
|
69
|
+
nameForConfig + `_${result.id}`
|
|
70
|
+
);
|
|
71
|
+
validName = promptName;
|
|
72
|
+
} else {
|
|
73
|
+
// Basic invalid name handling when force flag is passed
|
|
74
|
+
validName = nameForConfig + `_${result.id}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
updateAccountConfig({
|
|
80
|
+
...updatedConfig,
|
|
81
|
+
environment: updatedConfig.env,
|
|
82
|
+
tokenInfo: updatedConfig.auth.tokenInfo,
|
|
83
|
+
name: validName,
|
|
84
|
+
});
|
|
85
|
+
writeConfig();
|
|
86
|
+
|
|
87
|
+
logger.log('');
|
|
88
|
+
return validName;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const buildDeveloperTestAccount = async ({
|
|
92
|
+
name,
|
|
93
|
+
accountConfig,
|
|
94
|
+
env,
|
|
95
|
+
maxTestPortals,
|
|
96
|
+
force = false,
|
|
97
|
+
}) => {
|
|
98
|
+
SpinniesManager.init({
|
|
99
|
+
succeedColor: 'white',
|
|
100
|
+
});
|
|
101
|
+
const accountId = getAccountId(accountConfig.portalId);
|
|
102
|
+
|
|
103
|
+
let result;
|
|
104
|
+
const spinniesI18nKey = `${i18nKey}.create.loading`;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
logger.log('');
|
|
108
|
+
SpinniesManager.add('devTestAcctCreate', {
|
|
109
|
+
text: i18n(`${spinniesI18nKey}.add`, {
|
|
110
|
+
accountName: name,
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
result = await createDeveloperTestAccount(accountId, name);
|
|
114
|
+
|
|
115
|
+
SpinniesManager.succeed('devTestAcctCreate', {
|
|
116
|
+
text: i18n(`${spinniesI18nKey}.succeed`, {
|
|
117
|
+
name: result.accountName,
|
|
118
|
+
accountId: result.id,
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
} catch (err) {
|
|
122
|
+
debugErrorAndContext(err);
|
|
123
|
+
|
|
124
|
+
SpinniesManager.fail('devTestAcctCreate', {
|
|
125
|
+
text: i18n(`${spinniesI18nKey}.fail`, {
|
|
126
|
+
accountName: name,
|
|
127
|
+
}),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (isMissingScopeError(err)) {
|
|
131
|
+
logger.error(
|
|
132
|
+
i18n(`${i18nKey}.create.failure.scopes.message`, {
|
|
133
|
+
accountName: uiAccountDescription(accountId),
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
const websiteOrigin = getHubSpotWebsiteOrigin(env);
|
|
137
|
+
const url = `${websiteOrigin}/personal-access-key/${accountId}`;
|
|
138
|
+
logger.info(
|
|
139
|
+
i18n(`${i18nKey}.create.failure.scopes.instructions`, {
|
|
140
|
+
accountName: uiAccountDescription(accountId),
|
|
141
|
+
url,
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
} else if (
|
|
145
|
+
isSpecifiedError(err, {
|
|
146
|
+
statusCode: 400,
|
|
147
|
+
errorType: 'TEST_PORTAL_LIMIT_REACHED',
|
|
148
|
+
})
|
|
149
|
+
) {
|
|
150
|
+
logger.log('');
|
|
151
|
+
logger.error(
|
|
152
|
+
i18n(`${i18nKey}.create.failure.limit`, {
|
|
153
|
+
accountName: uiAccountDescription(accountId),
|
|
154
|
+
limit: maxTestPortals,
|
|
155
|
+
})
|
|
156
|
+
);
|
|
157
|
+
logger.log('');
|
|
158
|
+
} else {
|
|
159
|
+
logErrorInstance(err);
|
|
160
|
+
}
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let devTestAcctConfigName;
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// Response contains PAK, save to config here
|
|
168
|
+
devTestAcctConfigName = await saveDevTestAccountToConfig(
|
|
169
|
+
env,
|
|
170
|
+
result,
|
|
171
|
+
force
|
|
172
|
+
);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
logErrorInstance(err);
|
|
175
|
+
throw err;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
devTestAcctConfigName,
|
|
180
|
+
result,
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
module.exports = {
|
|
185
|
+
buildDeveloperTestAccount,
|
|
186
|
+
};
|
|
@@ -1,11 +1,57 @@
|
|
|
1
1
|
const {
|
|
2
2
|
HUBSPOT_ACCOUNT_TYPES,
|
|
3
3
|
} = require('@hubspot/local-dev-lib/constants/config');
|
|
4
|
+
const { getAccountId, getConfig } = require('@hubspot/local-dev-lib/config');
|
|
5
|
+
const { i18n } = require('./lang');
|
|
6
|
+
const {
|
|
7
|
+
fetchDeveloperTestAccounts,
|
|
8
|
+
} = require('@hubspot/local-dev-lib/developerTestAccounts');
|
|
9
|
+
|
|
10
|
+
const getHasDevTestAccounts = appDeveloperAccountConfig => {
|
|
11
|
+
const config = getConfig();
|
|
12
|
+
const parentPortalId = getAccountId(appDeveloperAccountConfig.portalId);
|
|
13
|
+
for (const portal of config.portals) {
|
|
14
|
+
if (
|
|
15
|
+
Boolean(portal.parentAccountId) &&
|
|
16
|
+
portal.parentAccountId === parentPortalId &&
|
|
17
|
+
portal.accountType === HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST
|
|
18
|
+
) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
};
|
|
4
24
|
|
|
5
|
-
const
|
|
6
|
-
config.accountType &&
|
|
7
|
-
config.accountType === HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST;
|
|
25
|
+
const i18nKey = 'cli.lib.developerTestAccount';
|
|
8
26
|
|
|
27
|
+
const validateDevTestAccountUsageLimits = async accountConfig => {
|
|
28
|
+
const accountId = getAccountId(accountConfig.portalId);
|
|
29
|
+
const response = await fetchDeveloperTestAccounts(accountId);
|
|
30
|
+
if (response) {
|
|
31
|
+
const limit = response.maxTestPortals;
|
|
32
|
+
const count = response.results.length;
|
|
33
|
+
if (count >= limit) {
|
|
34
|
+
const hasDevTestAccounts = getHasDevTestAccounts(accountConfig);
|
|
35
|
+
if (hasDevTestAccounts) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
i18n(`${i18nKey}.create.failure.alreadyInConfig`, {
|
|
38
|
+
accountName: accountConfig.name || accountId,
|
|
39
|
+
limit,
|
|
40
|
+
})
|
|
41
|
+
);
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error(
|
|
44
|
+
i18n(`${i18nKey}.create.failure.limit`, {
|
|
45
|
+
accountName: accountConfig.name || accountId,
|
|
46
|
+
limit,
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return response;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
9
55
|
module.exports = {
|
|
10
|
-
|
|
56
|
+
validateDevTestAccountUsageLimits,
|
|
11
57
|
};
|