@hubspot/cli 5.2.1-beta.0 → 5.2.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +6 -1
- package/commands/accounts/clean.js +3 -3
- package/commands/accounts/list.js +17 -5
- package/commands/init.js +1 -1
- package/commands/project/dev.js +108 -259
- package/commands/project/upload.js +3 -0
- package/commands/sandbox/sync.js +5 -3
- package/commands/theme/preview.js +48 -14
- package/lang/en.lyaml +55 -17
- package/lib/LocalDevManager.js +31 -33
- package/lib/accountTypes.js +34 -0
- package/lib/developerTestAccountCreate.js +187 -0
- package/lib/developerTestAccounts.js +50 -4
- package/lib/localDev.js +444 -0
- package/lib/projectStructure.js +12 -2
- package/lib/projects.js +36 -0
- package/lib/prompts/developerTestAccountNamePrompt.js +29 -0
- package/lib/prompts/previewPrompt.js +18 -0
- package/lib/prompts/projectDevTargetAccountPrompt.js +126 -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 +4 -4
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:
|
|
@@ -893,6 +882,7 @@ en:
|
|
|
893
882
|
describe: "Upload and watch a theme directory on your computer for changes and start a local development server to preview theme changes on a site"
|
|
894
883
|
errors:
|
|
895
884
|
invalidPath: "The path \"{{ path }}\" is not a path to a directory"
|
|
885
|
+
noThemeComponents: "Your project has no theme components available to preview."
|
|
896
886
|
options:
|
|
897
887
|
src:
|
|
898
888
|
describe: "Path to the local directory your theme is in, relative to your current working directory"
|
|
@@ -952,6 +942,26 @@ en:
|
|
|
952
942
|
setupError: "Failed to setup local dev server: {{ message }}"
|
|
953
943
|
startError: "Failed to start local dev server: {{ message }}"
|
|
954
944
|
fileChangeError: "Failed to notify local dev server of file change: {{ message }}"
|
|
945
|
+
localDev:
|
|
946
|
+
confirmDefaultAccountIsTarget:
|
|
947
|
+
declineDefaultAccountExplanation: "To develop on a different account, run {{ useCommand }} to change your default account, then re-run {{ devCommand }}."
|
|
948
|
+
checkIfAppDevloperAccount: "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
|
+
checkIfDeveloperTestAccount: "This project contains a public app. The \"--account\" flag must point to a developer test account to develop this project locally. Alternatively, change your default account to an App Developer Account using {{#bold}}`hs accounts use`{{/bold}} and run {{#bold}}`hs project dev`{{/bold}} to set up a new Developer Test Account."
|
|
950
|
+
suggestRecommendedNestedAccount:
|
|
951
|
+
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."
|
|
952
|
+
publicAppNonDeveloperTestAccountWarning: "Local development of public apps is only supported in {{#bold}}developer test accounts{{/bold}}."
|
|
953
|
+
privateAppNonDeveloperTestAccountWarning: "Local development of private apps is only supported in {{#bold}}developer test accounts{{/bold}}"
|
|
954
|
+
createNewProjectForLocalDev:
|
|
955
|
+
projectMustExistExplanation: "The project {{ projectName }} does not exist in the target account {{ accountIdentifier}}. This command requires the project to exist in the target account."
|
|
956
|
+
publicAppProjectMustExistExplanation: "The project {{ projectName }} does not exist in {{ accountIdentifier}}, the app developer account associated with your target account. This command requires the project to exist in this app developer account."
|
|
957
|
+
createProject: "Create new project {{ projectName}} in {{#bold}}[{{ accountIdentifier }}]{{/bold}}?"
|
|
958
|
+
choseNotToCreateProject: "Exiting because this command requires the project to exist in the target account."
|
|
959
|
+
creatingProject: "Creating project {{ projectName }} in {{ accountIdentifier }}"
|
|
960
|
+
createdProject: "Created project {{ projectName }} in {{ accountIdentifier }}"
|
|
961
|
+
failedToCreateProject: "Failed to create project in the target account."
|
|
962
|
+
createInitialBuildForNewProject:
|
|
963
|
+
initialUploadMessage: "HubSpot Local Dev Server Startup"
|
|
964
|
+
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
965
|
projects:
|
|
956
966
|
config:
|
|
957
967
|
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,11 +1075,14 @@ en:
|
|
|
1065
1075
|
prompts:
|
|
1066
1076
|
projectDevTargetAccountPrompt:
|
|
1067
1077
|
createNewSandboxOption: "<Test on a new development sandbox>"
|
|
1078
|
+
createNewDeveloperTestAccountOption: "<Test on a new developer test account>"
|
|
1068
1079
|
chooseDefaultAccountOption: "<{{#bold}}\U00002757{{/bold}} Test on this production account {{#bold}}\U00002757{{/bold}}>"
|
|
1069
|
-
promptMessage: "[--account] Choose a
|
|
1080
|
+
promptMessage: "[--account] Choose a {{ accountType }} under {{ accountIdentifier }} to test with:"
|
|
1070
1081
|
sandboxLimit: "Your account reached the limit of {{ limit }} development sandboxes"
|
|
1071
1082
|
sandboxLimitWithSuggestion: "Your account reached the limit of {{ limit }} development sandboxes. Run {{ authCommand }} to add an existing one to the config."
|
|
1083
|
+
developerTestAccountLimit: "Your account reached the limit of {{ limit }} developer test accounts."
|
|
1072
1084
|
confirmDefaultAccount: "Continue testing on {{#bold}}{{ accountName }} ({{ accountType }}){{/bold}}? (Y/n)"
|
|
1085
|
+
confirmUseExistingDeveloperTestAccount: "Continue with {{ accountName }}? This account isn't currently connected to the HubSpot CLI. By continuing, you'll be prompted to generate a personal access key and connect it."
|
|
1073
1086
|
projectLogsPrompt:
|
|
1074
1087
|
projectName:
|
|
1075
1088
|
message: "[--project] Enter the project name:"
|
|
@@ -1150,6 +1163,13 @@ en:
|
|
|
1150
1163
|
invalidTemplate: "[--template] Could not find template {{ template }}. Please choose an available template."
|
|
1151
1164
|
noProjectsInConfig: "Please ensure that there is a config.json file that contains a \"projects\" field."
|
|
1152
1165
|
missingPropertiesInConfig: "Please ensure that each of the projects in your config.json file contain the following properties: [\"name\", \"label\", \"path\", \"insertPath\"]."
|
|
1166
|
+
developerTestAccountPrompt:
|
|
1167
|
+
name:
|
|
1168
|
+
message: "Name your developer test account"
|
|
1169
|
+
errors:
|
|
1170
|
+
invalidName: "You entered an invalid name. Please try again."
|
|
1171
|
+
nameRequired: "The name may not be blank. Please try again."
|
|
1172
|
+
accountNameExists: "Account with name \"{{ name }}\" already exists in the CLI config, please enter a different name."
|
|
1153
1173
|
downloadProjectPrompt:
|
|
1154
1174
|
selectProject: "Select a project to download:"
|
|
1155
1175
|
errors:
|
|
@@ -1205,6 +1225,7 @@ en:
|
|
|
1205
1225
|
previewPrompt:
|
|
1206
1226
|
enterSrc: "[--src] Enter a local theme directory to preview."
|
|
1207
1227
|
enterDest: "[--dest] Enter the destination path for the src theme in HubSpot Design Tools."
|
|
1228
|
+
themeProjectSelect: "[--theme] Select which theme to preview."
|
|
1208
1229
|
errors:
|
|
1209
1230
|
srcRequired: "You must specify a source directory."
|
|
1210
1231
|
destRequired: "You must specify a destination directory."
|
|
@@ -1217,6 +1238,25 @@ en:
|
|
|
1217
1238
|
options:
|
|
1218
1239
|
options:
|
|
1219
1240
|
describe: "Options to pass to javascript fields files"
|
|
1241
|
+
developerTestAccount:
|
|
1242
|
+
create:
|
|
1243
|
+
loading:
|
|
1244
|
+
add: "Creating developer test account {{#bold}}{{ accountName }}{{/bold}}"
|
|
1245
|
+
fail: "Failed to create a developer test account {{#bold}}{{ accountName }}{{/bold}}."
|
|
1246
|
+
succeed: "Successfully created a developer test account {{#bold}}{{ name }}{{/bold}} with portalId {{#bold}}{{ accountId }}{{/bold}}."
|
|
1247
|
+
success:
|
|
1248
|
+
configFileUpdated: "Account \"{{ accountName }}\" updated using \"{{ authType }}\""
|
|
1249
|
+
failure:
|
|
1250
|
+
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}}."
|
|
1251
|
+
limit: "{{#bold}}{{ accountName }}{{/bold}} reached the limit of {{ limit }} developer test accounts.
|
|
1252
|
+
\n- To connect a developer test account to your HubSpot CLI, run {{#bold}}hs auth{{/bold}} and follow the prompts."
|
|
1253
|
+
alreadyInConfig: "{{#bold}}{{ accountName }}{{/bold}} reached the limit of {{ limit }} developer test accounts.
|
|
1254
|
+
\n- To use an existing developer test account, run {{#bold}}hs accounts use{{/bold}}."
|
|
1255
|
+
scopes:
|
|
1256
|
+
message: "The personal access key you provided doesn't include developer test account permissions."
|
|
1257
|
+
instructions: "To update CLI permissions for \"{{ accountName }}\":
|
|
1258
|
+
\n- Go to {{ url }}, deactivate the existing personal access key, and create a new one that includes developer test account permissions.
|
|
1259
|
+
\n- Update the CLI config for this account by running {{#bold}}hs auth{{/bold}} and entering the new key.\n"
|
|
1220
1260
|
sandbox:
|
|
1221
1261
|
create:
|
|
1222
1262
|
loading:
|
|
@@ -1228,11 +1268,9 @@ en:
|
|
|
1228
1268
|
add: "Creating standard sandbox {{#bold}}{{ sandboxName }}{{/bold}}"
|
|
1229
1269
|
fail: "Failed to create a standard sandbox {{#bold}}{{ sandboxName }}{{/bold}}."
|
|
1230
1270
|
succeed: "Successfully created a standard sandbox {{#bold}}{{ name }}{{/bold}} with portalId {{#bold}}{{ sandboxHubId }}{{/bold}}."
|
|
1231
|
-
success:
|
|
1232
|
-
configFileUpdated: "{{ configFilename }} updated with {{ authMethod }} for account {{ account }}."
|
|
1233
1271
|
failure:
|
|
1234
1272
|
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/
|
|
1273
|
+
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
1274
|
limit:
|
|
1237
1275
|
developer:
|
|
1238
1276
|
one: "{{#bold}}{{ accountName }}{{/bold}} reached the limit of {{ limit }} development sandbox.
|
|
@@ -1336,7 +1374,7 @@ en:
|
|
|
1336
1374
|
400: "The {{ messageDetail }} was bad."
|
|
1337
1375
|
401: "The {{ messageDetail }} was unauthorized."
|
|
1338
1376
|
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/
|
|
1377
|
+
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
1378
|
403: "The {{ messageDetail }} was forbidden."
|
|
1341
1379
|
404Request: 'The {{ action }} failed because "{{ request }}" was not found in account {{ accountId }}.'
|
|
1342
1380
|
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,187 @@
|
|
|
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
|
+
saveDevTestAccountToConfig,
|
|
187
|
+
};
|
|
@@ -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
|
};
|