@hubspot/cli 7.6.2-beta.0 → 7.7.0-beta.0
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/commands/config/set.d.ts +1 -1
- package/commands/config/set.js +65 -33
- package/commands/init.js +0 -1
- package/commands/project/__tests__/validate.test.d.ts +1 -0
- package/commands/project/__tests__/validate.test.js +98 -0
- package/commands/project/validate.js +4 -4
- package/commands/testAccount/__tests__/delete.test.js +2 -4
- package/commands/testAccount/delete.d.ts +4 -3
- package/commands/testAccount/delete.js +155 -14
- package/lang/en.d.ts +37 -8
- package/lang/en.js +49 -20
- package/lib/__tests__/yargsUtils.test.js +83 -9
- package/lib/configOptions.js +7 -0
- package/lib/constants.d.ts +6 -0
- package/lib/constants.js +10 -0
- package/lib/doctor/DiagnosticInfoBuilder.js +7 -6
- package/lib/projects/__tests__/AppDevModeInterface.test.js +2 -0
- package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +64 -33
- package/lib/projects/__tests__/localDevProjectHelpers.test.js +2 -0
- package/lib/projects/__tests__/upload.test.d.ts +1 -0
- package/lib/projects/__tests__/upload.test.js +82 -0
- package/lib/projects/localDev/AppDevModeInterface.d.ts +2 -0
- package/lib/projects/localDev/AppDevModeInterface.js +17 -8
- package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +0 -1
- package/lib/projects/localDev/LocalDevWebsocketServer.js +4 -7
- package/lib/projects/structure.js +4 -4
- package/lib/projects/upload.d.ts +1 -1
- package/lib/projects/upload.js +15 -6
- package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +10 -1
- package/lib/prompts/promptUtils.js +8 -5
- package/lib/yargsUtils.d.ts +1 -1
- package/lib/yargsUtils.js +12 -5
- package/package.json +2 -2
package/commands/config/set.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CmsPublishMode } from '@hubspot/local-dev-lib/types/Files';
|
|
2
2
|
import { CommonArgs, ConfigArgs, YargsCommandModule } from '../../types/Yargs.js';
|
|
3
3
|
type ConfigSetArgs = CommonArgs & ConfigArgs & {
|
|
4
|
-
defaultCmsPublishMode
|
|
4
|
+
defaultCmsPublishMode?: CmsPublishMode;
|
|
5
5
|
allowUsageTracking?: boolean;
|
|
6
6
|
httpTimeout?: string;
|
|
7
7
|
allowAutoUpdates?: boolean;
|
package/commands/config/set.js
CHANGED
|
@@ -4,7 +4,9 @@ import { promptUser } from '../../lib/prompts/promptUtils.js';
|
|
|
4
4
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
5
5
|
import { setDefaultCmsPublishMode, setHttpTimeout, setAllowUsageTracking, setAllowAutoUpdates, setAutoOpenBrowser, } from '../../lib/configOptions.js';
|
|
6
6
|
import { commands } from '../../lang/en.js';
|
|
7
|
-
import { makeYargsBuilder,
|
|
7
|
+
import { makeYargsBuilder, strictEnforceBoolean, } from '../../lib/yargsUtils.js';
|
|
8
|
+
import { logError } from '../../lib/errorHandlers/index.js';
|
|
9
|
+
import { uiLogger } from '../../lib/ui/logger.js';
|
|
8
10
|
const command = 'set';
|
|
9
11
|
const describe = commands.config.subcommands.set.describe;
|
|
10
12
|
async function selectOptions() {
|
|
@@ -29,35 +31,41 @@ async function selectOptions() {
|
|
|
29
31
|
}
|
|
30
32
|
async function handleConfigUpdate(accountId, args) {
|
|
31
33
|
const { allowAutoUpdates, allowUsageTracking, defaultCmsPublishMode, httpTimeout, autoOpenBrowser, } = args;
|
|
32
|
-
if (
|
|
33
|
-
await
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
else if (typeof httpTimeout !== 'undefined') {
|
|
37
|
-
await setHttpTimeout({ httpTimeout, accountId });
|
|
38
|
-
return true;
|
|
34
|
+
if (allowAutoUpdates !== undefined) {
|
|
35
|
+
await setAllowAutoUpdates({ allowAutoUpdates, accountId });
|
|
39
36
|
}
|
|
40
|
-
|
|
37
|
+
if (allowUsageTracking !== undefined) {
|
|
41
38
|
await setAllowUsageTracking({ allowUsageTracking, accountId });
|
|
42
|
-
return true;
|
|
43
39
|
}
|
|
44
|
-
|
|
45
|
-
await setAllowAutoUpdates({ allowAutoUpdates, accountId });
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
else if (typeof autoOpenBrowser !== 'undefined') {
|
|
40
|
+
if (autoOpenBrowser !== undefined) {
|
|
49
41
|
await setAutoOpenBrowser({ autoOpenBrowser, accountId });
|
|
50
|
-
return true;
|
|
51
42
|
}
|
|
52
|
-
|
|
43
|
+
if (defaultCmsPublishMode !== undefined) {
|
|
44
|
+
await setDefaultCmsPublishMode({ defaultCmsPublishMode, accountId });
|
|
45
|
+
}
|
|
46
|
+
if (httpTimeout !== undefined) {
|
|
47
|
+
await setHttpTimeout({ httpTimeout, accountId });
|
|
48
|
+
}
|
|
53
49
|
}
|
|
54
50
|
async function handler(args) {
|
|
55
|
-
const { derivedAccountId } = args;
|
|
51
|
+
const { derivedAccountId, allowAutoUpdates, allowUsageTracking, defaultCmsPublishMode, httpTimeout, autoOpenBrowser, } = args;
|
|
56
52
|
trackCommandUsage('config-set', {}, derivedAccountId);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
try {
|
|
54
|
+
if (allowAutoUpdates !== undefined ||
|
|
55
|
+
allowUsageTracking !== undefined ||
|
|
56
|
+
autoOpenBrowser !== undefined ||
|
|
57
|
+
defaultCmsPublishMode !== undefined ||
|
|
58
|
+
httpTimeout !== undefined) {
|
|
59
|
+
await handleConfigUpdate(derivedAccountId, args);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const selectedOptions = await selectOptions();
|
|
63
|
+
await handleConfigUpdate(derivedAccountId, selectedOptions);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
logError(err);
|
|
68
|
+
process.exit(EXIT_CODES.ERROR);
|
|
61
69
|
}
|
|
62
70
|
process.exit(EXIT_CODES.SUCCESS);
|
|
63
71
|
}
|
|
@@ -67,15 +75,23 @@ function configSetBuilder(yargs) {
|
|
|
67
75
|
'default-cms-publish-mode': {
|
|
68
76
|
describe: commands.config.subcommands.set.options.defaultMode.describe,
|
|
69
77
|
type: 'string',
|
|
78
|
+
choices: ['draft', 'publish'],
|
|
79
|
+
},
|
|
80
|
+
'http-timeout': {
|
|
81
|
+
describe: commands.config.subcommands.set.options.httpTimeout.describe,
|
|
82
|
+
type: 'number',
|
|
83
|
+
coerce: (value) => {
|
|
84
|
+
if (isNaN(value) || value < 3000) {
|
|
85
|
+
uiLogger.error(commands.config.subcommands.set.errors.invalidHTTPTimeout);
|
|
86
|
+
process.exit(EXIT_CODES.ERROR);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
},
|
|
70
90
|
},
|
|
71
91
|
'allow-usage-tracking': {
|
|
72
92
|
describe: commands.config.subcommands.set.options.allowUsageTracking.describe,
|
|
73
93
|
type: 'boolean',
|
|
74
94
|
},
|
|
75
|
-
'http-timeout': {
|
|
76
|
-
describe: commands.config.subcommands.set.options.httpTimeout.describe,
|
|
77
|
-
type: 'string',
|
|
78
|
-
},
|
|
79
95
|
'allow-auto-updates': {
|
|
80
96
|
describe: commands.config.subcommands.set.options.allowAutoUpdates.describe,
|
|
81
97
|
type: 'boolean',
|
|
@@ -86,18 +102,34 @@ function configSetBuilder(yargs) {
|
|
|
86
102
|
type: 'boolean',
|
|
87
103
|
},
|
|
88
104
|
})
|
|
89
|
-
.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
.check(() => {
|
|
106
|
+
// Use process.argv directly because yargs argv has already been parsed/coerced,
|
|
107
|
+
// but we need to validate the exact format the user provided (e.g., --flag=true vs --flag)
|
|
108
|
+
return strictEnforceBoolean(process.argv, [
|
|
109
|
+
'allow-usage-tracking',
|
|
110
|
+
'allow-auto-updates',
|
|
111
|
+
'auto-open-browser',
|
|
112
|
+
]);
|
|
113
|
+
})
|
|
96
114
|
.example([
|
|
97
115
|
[
|
|
98
116
|
'$0 config set',
|
|
99
117
|
i18n(`commands.config.subcommands.set.examples.default`),
|
|
100
118
|
],
|
|
119
|
+
['$0 config set --allow-usage-tracking=false', 'Disable usage tracking'],
|
|
120
|
+
['$0 config set --http-timeout=5000', 'Set HTTP timeout to 5000ms'],
|
|
121
|
+
[
|
|
122
|
+
'$0 config set --default-cms-publish-mode=draft',
|
|
123
|
+
'Set default CMS publish mode to draft',
|
|
124
|
+
],
|
|
125
|
+
[
|
|
126
|
+
'$0 config set --http-timeout=3000 --allow-usage-tracking=false',
|
|
127
|
+
'Set HTTP timeout and disable usage tracking',
|
|
128
|
+
],
|
|
129
|
+
[
|
|
130
|
+
'$0 config set --default-cms-publish-mode=draft --http-timeout=4000 --allow-usage-tracking=true',
|
|
131
|
+
'Configure multiple settings at once',
|
|
132
|
+
],
|
|
101
133
|
]);
|
|
102
134
|
return yargs;
|
|
103
135
|
}
|
package/commands/init.js
CHANGED
|
@@ -99,7 +99,6 @@ async function handler(args) {
|
|
|
99
99
|
uiLogger.error(commands.init.errors.bothConfigFilesNotAllowed(path));
|
|
100
100
|
process.exit(EXIT_CODES.ERROR);
|
|
101
101
|
}
|
|
102
|
-
trackAuthAction('init', authType, TRACKING_STATUS.STARTED, parsedUserProvidedAccountId);
|
|
103
102
|
createEmptyConfigFile({ path: configPath }, useHiddenConfig);
|
|
104
103
|
//Needed to load deprecated config
|
|
105
104
|
loadConfig(configPath, args);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { vi } from 'vitest';
|
|
3
|
+
import { validateSourceDirectory } from '../../../lib/projects/upload.js';
|
|
4
|
+
import { getProjectConfig, validateProjectConfig, } from '../../../lib/projects/config.js';
|
|
5
|
+
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
6
|
+
import { commands } from '../../../lang/en.js';
|
|
7
|
+
import { useV3Api } from '../../../lib/projects/platformVersion.js';
|
|
8
|
+
import { loadAndValidateProfile } from '../../../lib/projectProfiles.js';
|
|
9
|
+
import { trackCommandUsage } from '../../../lib/usageTracking.js';
|
|
10
|
+
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
|
|
11
|
+
import { handleTranslate } from '../../../lib/projects/upload.js';
|
|
12
|
+
import projectValidateCommand from '../validate.js';
|
|
13
|
+
// Mock dependencies
|
|
14
|
+
vi.mock('../../../lib/projects/upload.js');
|
|
15
|
+
vi.mock('../../../lib/projects/config.js');
|
|
16
|
+
vi.mock('../../../lib/ui/logger.js');
|
|
17
|
+
vi.mock('../../../lib/usageTracking.js');
|
|
18
|
+
vi.mock('../../../lib/projectProfiles.js');
|
|
19
|
+
vi.mock('../../../lib/errorHandlers/index.js');
|
|
20
|
+
vi.mock('@hubspot/local-dev-lib/config');
|
|
21
|
+
vi.mock('../../../lib/projects/platformVersion.js');
|
|
22
|
+
describe('commands/project/validate', () => {
|
|
23
|
+
const projectDir = '/test/project';
|
|
24
|
+
let exitSpy;
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
// Mock process.exit to throw to stop execution
|
|
27
|
+
exitSpy = vi.spyOn(process, 'exit').mockImplementation(code => {
|
|
28
|
+
throw new Error(`Process exited with code ${code}`);
|
|
29
|
+
});
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
exitSpy.mockRestore();
|
|
34
|
+
});
|
|
35
|
+
describe('project configuration validation', () => {
|
|
36
|
+
it('should exit with error when project config is null', async () => {
|
|
37
|
+
vi.mocked(getProjectConfig).mockResolvedValue({
|
|
38
|
+
projectConfig: null,
|
|
39
|
+
projectDir: null,
|
|
40
|
+
});
|
|
41
|
+
await expect(
|
|
42
|
+
// @ts-expect-error partial mock
|
|
43
|
+
projectValidateCommand.handler({
|
|
44
|
+
derivedAccountId: 123,
|
|
45
|
+
d: false,
|
|
46
|
+
debug: false,
|
|
47
|
+
})).rejects.toThrow('Process exited with code 1');
|
|
48
|
+
expect(uiLogger.error).toHaveBeenCalledWith(commands.project.validate.mustBeRanWithinAProject);
|
|
49
|
+
});
|
|
50
|
+
it('should exit with error when project directory is null', async () => {
|
|
51
|
+
vi.mocked(getProjectConfig).mockResolvedValue({
|
|
52
|
+
projectConfig: {
|
|
53
|
+
name: 'test',
|
|
54
|
+
srcDir: 'src',
|
|
55
|
+
platformVersion: '2025.2',
|
|
56
|
+
},
|
|
57
|
+
projectDir: null,
|
|
58
|
+
});
|
|
59
|
+
await expect(
|
|
60
|
+
// @ts-expect-error partial mock
|
|
61
|
+
projectValidateCommand.handler({
|
|
62
|
+
derivedAccountId: 123,
|
|
63
|
+
d: false,
|
|
64
|
+
debug: false,
|
|
65
|
+
})).rejects.toThrow('Process exited with code 1');
|
|
66
|
+
expect(uiLogger.error).toHaveBeenCalledWith(commands.project.validate.mustBeRanWithinAProject);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
it('should call validateSourceDirectory with correct parameters', async () => {
|
|
70
|
+
const mockProjectConfig = {
|
|
71
|
+
name: 'test-project',
|
|
72
|
+
srcDir: 'src',
|
|
73
|
+
platformVersion: '2025.2',
|
|
74
|
+
};
|
|
75
|
+
vi.mocked(getProjectConfig).mockResolvedValue({
|
|
76
|
+
projectConfig: mockProjectConfig,
|
|
77
|
+
projectDir,
|
|
78
|
+
});
|
|
79
|
+
vi.mocked(useV3Api).mockReturnValue(true);
|
|
80
|
+
vi.mocked(validateProjectConfig).mockReturnValue(undefined);
|
|
81
|
+
vi.mocked(loadAndValidateProfile).mockResolvedValue(123);
|
|
82
|
+
vi.mocked(getAccountConfig).mockReturnValue({
|
|
83
|
+
accountType: 'STANDARD',
|
|
84
|
+
accountId: 123,
|
|
85
|
+
env: 'prod',
|
|
86
|
+
});
|
|
87
|
+
vi.mocked(trackCommandUsage);
|
|
88
|
+
vi.mocked(validateSourceDirectory).mockResolvedValue(undefined);
|
|
89
|
+
vi.mocked(handleTranslate).mockResolvedValue(undefined);
|
|
90
|
+
await expect(projectValidateCommand.handler({
|
|
91
|
+
derivedAccountId: 123,
|
|
92
|
+
d: false,
|
|
93
|
+
debug: false,
|
|
94
|
+
})).rejects.toThrow('Process exited with code 0');
|
|
95
|
+
const expectedSrcDir = path.resolve(projectDir, mockProjectConfig.srcDir);
|
|
96
|
+
expect(validateSourceDirectory).toHaveBeenCalledWith(expectedSrcDir, mockProjectConfig, projectDir);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -3,10 +3,10 @@ import { getAccountConfig } from '@hubspot/local-dev-lib/config';
|
|
|
3
3
|
import { useV3Api } from '../../lib/projects/platformVersion.js';
|
|
4
4
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
5
5
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
6
|
-
import { getProjectConfig, validateProjectConfig
|
|
6
|
+
import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
|
|
7
7
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
8
8
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
9
|
-
import { validateSourceDirectory
|
|
9
|
+
import { validateSourceDirectory, handleTranslate, } from '../../lib/projects/upload.js';
|
|
10
10
|
import { commands } from '../../lang/en.js';
|
|
11
11
|
import { loadAndValidateProfile } from '../../lib/projectProfiles.js';
|
|
12
12
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
@@ -15,7 +15,7 @@ const describe = commands.project.validate.describe;
|
|
|
15
15
|
async function handler(args) {
|
|
16
16
|
const { derivedAccountId, profile } = args;
|
|
17
17
|
const { projectConfig, projectDir } = await getProjectConfig();
|
|
18
|
-
if (!projectConfig) {
|
|
18
|
+
if (!projectConfig || !projectDir) {
|
|
19
19
|
uiLogger.error(commands.project.validate.mustBeRanWithinAProject);
|
|
20
20
|
process.exit(EXIT_CODES.ERROR);
|
|
21
21
|
}
|
|
@@ -31,7 +31,7 @@ async function handler(args) {
|
|
|
31
31
|
trackCommandUsage('project-validate', { type: accountType }, targetAccountId);
|
|
32
32
|
const srcDir = path.resolve(projectDir, projectConfig.srcDir);
|
|
33
33
|
try {
|
|
34
|
-
validateSourceDirectory(srcDir, projectConfig);
|
|
34
|
+
await validateSourceDirectory(srcDir, projectConfig, projectDir);
|
|
35
35
|
}
|
|
36
36
|
catch (e) {
|
|
37
37
|
logError(e);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import yargs from 'yargs';
|
|
2
|
-
import {
|
|
2
|
+
import { addConfigOptions, addUseEnvironmentOptions, addTestingOptions, } from '../../../lib/commonOpts.js';
|
|
3
3
|
import testAccountDeleteCommand from '../delete.js';
|
|
4
4
|
vi.mock('../../../lib/commonOpts');
|
|
5
5
|
describe('commands/testAccount/delete', () => {
|
|
6
6
|
const yargsMock = yargs;
|
|
7
7
|
describe('command', () => {
|
|
8
8
|
it('should have the correct command structure', () => {
|
|
9
|
-
expect(testAccountDeleteCommand.command).toEqual('delete
|
|
9
|
+
expect(testAccountDeleteCommand.command).toEqual('delete [test-account]');
|
|
10
10
|
});
|
|
11
11
|
});
|
|
12
12
|
describe('describe', () => {
|
|
@@ -20,8 +20,6 @@ describe('commands/testAccount/delete', () => {
|
|
|
20
20
|
expect(yargsMock.example).toHaveBeenCalledTimes(1);
|
|
21
21
|
expect(addTestingOptions).toHaveBeenCalledTimes(1);
|
|
22
22
|
expect(addTestingOptions).toHaveBeenCalledWith(yargsMock);
|
|
23
|
-
expect(addAccountOptions).toHaveBeenCalledTimes(1);
|
|
24
|
-
expect(addAccountOptions).toHaveBeenCalledWith(yargsMock);
|
|
25
23
|
expect(addConfigOptions).toHaveBeenCalledTimes(1);
|
|
26
24
|
expect(addConfigOptions).toHaveBeenCalledWith(yargsMock);
|
|
27
25
|
expect(addUseEnvironmentOptions).toHaveBeenCalledTimes(1);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { CommonArgs, ConfigArgs,
|
|
2
|
-
type DeleteTestAccountArgs = CommonArgs &
|
|
3
|
-
|
|
1
|
+
import { CommonArgs, ConfigArgs, EnvironmentArgs, TestingArgs, YargsCommandModule } from '../../types/Yargs.js';
|
|
2
|
+
type DeleteTestAccountArgs = CommonArgs & ConfigArgs & TestingArgs & EnvironmentArgs & {
|
|
3
|
+
testAccount?: string | number;
|
|
4
|
+
force?: boolean;
|
|
4
5
|
};
|
|
5
6
|
declare const deleteTestAccountCommand: YargsCommandModule<unknown, DeleteTestAccountArgs>;
|
|
6
7
|
export default deleteTestAccountCommand;
|
|
@@ -1,39 +1,180 @@
|
|
|
1
|
-
import { deleteDeveloperTestAccount } from '@hubspot/local-dev-lib/api/developerTestAccounts';
|
|
1
|
+
import { fetchDeveloperTestAccounts, deleteDeveloperTestAccount, } from '@hubspot/local-dev-lib/api/developerTestAccounts';
|
|
2
2
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
3
3
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
4
4
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
5
5
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
6
6
|
import { commands } from '../../lang/en.js';
|
|
7
|
-
|
|
7
|
+
import { deleteAccount, getAccountConfig, getAccountId, getConfigPath, loadConfig, updateDefaultAccount, } from '@hubspot/local-dev-lib/config';
|
|
8
|
+
import { promptUser } from '../../lib/prompts/promptUtils.js';
|
|
9
|
+
import { debugError } from '../../lib/errorHandlers/index.js';
|
|
10
|
+
const command = 'delete [test-account]';
|
|
8
11
|
const describe = commands.testAccount.delete.describe;
|
|
9
|
-
async function
|
|
10
|
-
const { derivedAccountId, testAccountId } = args;
|
|
11
|
-
trackCommandUsage('test-account-delete', {}, derivedAccountId);
|
|
12
|
+
async function getAccountPromptOptions(derivedAccountId) {
|
|
12
13
|
try {
|
|
13
|
-
await
|
|
14
|
-
|
|
14
|
+
const { data } = await fetchDeveloperTestAccounts(derivedAccountId);
|
|
15
|
+
return data.results.map(testAccount => ({
|
|
16
|
+
name: `${testAccount.accountName} (${testAccount.id})`,
|
|
17
|
+
value: testAccount.id,
|
|
18
|
+
}));
|
|
15
19
|
}
|
|
16
20
|
catch (err) {
|
|
17
|
-
uiLogger.error(commands.testAccount.delete.errors.
|
|
21
|
+
uiLogger.error(commands.testAccount.delete.errors.failedToFetchTestAccounts);
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function accountToDeleteSelectionPrompt(derivedAccountId) {
|
|
26
|
+
const accountData = await getAccountPromptOptions(derivedAccountId);
|
|
27
|
+
if (accountData.length === 0) {
|
|
28
|
+
uiLogger.error(commands.testAccount.delete.errors.noAccountsToDelete(derivedAccountId));
|
|
29
|
+
process.exit(EXIT_CODES.ERROR);
|
|
30
|
+
}
|
|
31
|
+
const { testAccountToDelete } = await promptUser([
|
|
32
|
+
{
|
|
33
|
+
type: 'list',
|
|
34
|
+
name: 'testAccountToDelete',
|
|
35
|
+
pageSize: 20,
|
|
36
|
+
message: commands.testAccount.delete.prompts.selectTestAccounts,
|
|
37
|
+
choices: accountData,
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
return testAccountToDelete;
|
|
41
|
+
}
|
|
42
|
+
async function confirmDeletion() {
|
|
43
|
+
const { shouldDelete } = await promptUser([
|
|
44
|
+
{
|
|
45
|
+
type: 'confirm',
|
|
46
|
+
name: 'shouldDelete',
|
|
47
|
+
message: commands.testAccount.delete.prompts.confirmDeletion,
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
return shouldDelete;
|
|
51
|
+
}
|
|
52
|
+
async function deleteTestAccountInHubSpot(derivedAccountId, accountId) {
|
|
53
|
+
try {
|
|
54
|
+
await deleteDeveloperTestAccount(derivedAccountId, accountId, true);
|
|
55
|
+
uiLogger.success(commands.testAccount.delete.success.testAccountDeletedFromHubSpot(accountId));
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
debugError(e);
|
|
59
|
+
uiLogger.error(commands.testAccount.delete.errors.failedToDelete(accountId));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function deleteTestAccountFromConfig(testAccountId, parentAccountName, account) {
|
|
63
|
+
try {
|
|
64
|
+
// If the account isn't in the local config then it wasn't auth'd on the local machine
|
|
65
|
+
if (account && account.name && account.accountType) {
|
|
66
|
+
// If the deleted test account was the default account, replace the default account with the parent account
|
|
67
|
+
loadConfig(getConfigPath()); // Get updated version of the config
|
|
68
|
+
const defaultAccountId = getAccountId(); // We need to get the current default accountId before delete the test account
|
|
69
|
+
await deleteAccount(account.name);
|
|
70
|
+
uiLogger.success(commands.testAccount.delete.success.testAccountDeletedFromConfig(testAccountId));
|
|
71
|
+
if (testAccountId === defaultAccountId) {
|
|
72
|
+
updateDefaultAccount(parentAccountName);
|
|
73
|
+
uiLogger.info(commands.testAccount.delete.info.replaceDefaultAccount(testAccountId, parentAccountName));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
debugError(e);
|
|
79
|
+
uiLogger.error(commands.testAccount.delete.errors.failedToDeleteFromConfig(testAccountId));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function validateTestAccountConfigs(testAccountId) {
|
|
83
|
+
if (!testAccountId) {
|
|
84
|
+
uiLogger.error(commands.testAccount.delete.errors.testAccountNotFound(testAccountId));
|
|
85
|
+
process.exit(EXIT_CODES.ERROR);
|
|
86
|
+
}
|
|
87
|
+
const testAccountConfig = getAccountConfig(testAccountId);
|
|
88
|
+
if (!testAccountConfig) {
|
|
89
|
+
uiLogger.error(commands.testAccount.delete.errors.testAccountNotFound(testAccountId));
|
|
18
90
|
process.exit(EXIT_CODES.ERROR);
|
|
19
91
|
}
|
|
92
|
+
const parentAccountConfig = getAccountConfig(testAccountConfig.parentAccountId);
|
|
93
|
+
if (!parentAccountConfig) {
|
|
94
|
+
uiLogger.error(commands.testAccount.delete.errors.parentAccountNotFound(testAccountId));
|
|
95
|
+
process.exit(EXIT_CODES.ERROR);
|
|
96
|
+
}
|
|
97
|
+
const parentAccountName = parentAccountConfig.name;
|
|
98
|
+
return { testAccountConfig, parentAccountName };
|
|
99
|
+
}
|
|
100
|
+
async function handler(args) {
|
|
101
|
+
const { derivedAccountId, testAccount, force } = args;
|
|
102
|
+
trackCommandUsage('test-account-delete', {}, derivedAccountId);
|
|
103
|
+
let testAccountIdToDelete = 0;
|
|
104
|
+
// See if the account exists
|
|
105
|
+
if (testAccount) {
|
|
106
|
+
const accountId = getAccountId(testAccount);
|
|
107
|
+
await validateTestAccountConfigs(accountId);
|
|
108
|
+
if (accountId) {
|
|
109
|
+
testAccountIdToDelete = accountId;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Prompt for selection when name or id aren't provided
|
|
113
|
+
if (!testAccountIdToDelete) {
|
|
114
|
+
try {
|
|
115
|
+
testAccountIdToDelete =
|
|
116
|
+
await accountToDeleteSelectionPrompt(derivedAccountId);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
debugError(err);
|
|
120
|
+
uiLogger.error(commands.testAccount.delete.errors.failedToSelectAccount);
|
|
121
|
+
process.exit(EXIT_CODES.ERROR);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const { testAccountConfig, parentAccountName } = await validateTestAccountConfigs(testAccountIdToDelete);
|
|
125
|
+
// If --force, don't prompt user; else confirm deletion
|
|
126
|
+
let shouldDeleteAccount;
|
|
127
|
+
if (force) {
|
|
128
|
+
shouldDeleteAccount = true;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
shouldDeleteAccount = await confirmDeletion();
|
|
132
|
+
}
|
|
133
|
+
if (shouldDeleteAccount) {
|
|
134
|
+
const parentAccountId = testAccountConfig.parentAccountId;
|
|
135
|
+
await deleteTestAccountInHubSpot(parentAccountId, testAccountIdToDelete);
|
|
136
|
+
await deleteTestAccountFromConfig(testAccountIdToDelete, parentAccountName, testAccountConfig);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
uiLogger.info(commands.testAccount.delete.info.deletionCanceled);
|
|
140
|
+
}
|
|
20
141
|
process.exit(EXIT_CODES.SUCCESS);
|
|
21
142
|
}
|
|
22
143
|
function deleteTestAccountBuilder(yargs) {
|
|
23
|
-
yargs.positional('test-account
|
|
24
|
-
type: '
|
|
25
|
-
description: commands.testAccount.delete.
|
|
26
|
-
required:
|
|
144
|
+
yargs.positional('test-account', {
|
|
145
|
+
type: 'string',
|
|
146
|
+
description: commands.testAccount.delete.options.id,
|
|
147
|
+
required: false,
|
|
148
|
+
});
|
|
149
|
+
yargs.option('force', {
|
|
150
|
+
describe: commands.upload.options.force,
|
|
151
|
+
type: 'boolean',
|
|
152
|
+
default: false,
|
|
27
153
|
});
|
|
28
154
|
yargs.example([
|
|
29
|
-
[
|
|
155
|
+
[
|
|
156
|
+
'$0 test-account delete 12345678',
|
|
157
|
+
commands.testAccount.delete.examples.withPositionalID(12345678),
|
|
158
|
+
],
|
|
159
|
+
[
|
|
160
|
+
'$0 test-account delete my-test-account',
|
|
161
|
+
commands.testAccount.delete.examples.withPositionalName('my-test-account'),
|
|
162
|
+
],
|
|
163
|
+
[
|
|
164
|
+
'$0 test-account delete --test-account=12345678',
|
|
165
|
+
commands.testAccount.delete.examples.withID(12345678),
|
|
166
|
+
],
|
|
167
|
+
[
|
|
168
|
+
'$0 test-account delete --test-account=my-test-account',
|
|
169
|
+
commands.testAccount.delete.examples.withName('my-test-account'),
|
|
170
|
+
],
|
|
171
|
+
['$0 test-account delete', commands.testAccount.delete.examples.withoutId],
|
|
30
172
|
]);
|
|
31
173
|
return yargs;
|
|
32
174
|
}
|
|
33
175
|
const builder = makeYargsBuilder(deleteTestAccountBuilder, command, describe, {
|
|
34
176
|
useGlobalOptions: true,
|
|
35
177
|
useEnvironmentOptions: true,
|
|
36
|
-
useAccountOptions: true,
|
|
37
178
|
useConfigOptions: true,
|
|
38
179
|
useTestingOptions: true,
|
|
39
180
|
});
|
package/lang/en.d.ts
CHANGED
|
@@ -260,6 +260,10 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
|
|
|
260
260
|
readonly describe: "Enable or disable automatic opening of the browser";
|
|
261
261
|
};
|
|
262
262
|
};
|
|
263
|
+
readonly errors: {
|
|
264
|
+
readonly invalidBoolean: (commandName: string, value: string) => string;
|
|
265
|
+
readonly invalidHTTPTimeout: "Invalid HTTP timeout value. Must be a number greater than 3000.";
|
|
266
|
+
};
|
|
263
267
|
};
|
|
264
268
|
};
|
|
265
269
|
};
|
|
@@ -1907,18 +1911,41 @@ ${string}`;
|
|
|
1907
1911
|
readonly example: (name: string) => string;
|
|
1908
1912
|
};
|
|
1909
1913
|
readonly delete: {
|
|
1910
|
-
readonly describe: "Delete a test account config
|
|
1914
|
+
readonly describe: "Delete a test account from your HubSpot account and CLI config";
|
|
1911
1915
|
readonly pathPrompt: "[--path] What is the path to the test account config?";
|
|
1916
|
+
readonly info: {
|
|
1917
|
+
readonly deletionCanceled: "Deletion canceled by user";
|
|
1918
|
+
readonly accountNotFoundWithId: (id: number) => string;
|
|
1919
|
+
readonly replaceDefaultAccount: (testAccountId: number, parentAccountName: string) => string;
|
|
1920
|
+
};
|
|
1921
|
+
readonly prompts: {
|
|
1922
|
+
readonly selectTestAccounts: "Select test account(s) to delete";
|
|
1923
|
+
readonly confirmDeletion: "All data for the account will be permanently deleted. Any connected apps will have their access tokens revoked. Do you wish to proceed?";
|
|
1924
|
+
};
|
|
1912
1925
|
readonly errors: {
|
|
1913
|
-
readonly failedToDelete:
|
|
1926
|
+
readonly failedToDelete: (testAccountToDelete: number) => string;
|
|
1927
|
+
readonly failedToSelectAccount: "Failed to select a test account to delete";
|
|
1928
|
+
readonly noAccountsToDelete: (accountId: number) => string;
|
|
1929
|
+
readonly failedToDeleteFromConfig: (testAccountToDelete: number) => string;
|
|
1930
|
+
readonly failedToFetchTestAccounts: "Failed to fetch developer test accounts";
|
|
1931
|
+
readonly testAccountNotFound: (nameOrId: string | number | null) => string;
|
|
1932
|
+
readonly parentAccountNotFound: (testAccountId: number) => string;
|
|
1914
1933
|
};
|
|
1915
1934
|
readonly success: {
|
|
1916
|
-
readonly
|
|
1935
|
+
readonly testAccountDeletedFromHubSpot: (testAccountToDelete: number) => string;
|
|
1936
|
+
readonly testAccountDeletedFromConfig: (accountId: number) => string;
|
|
1937
|
+
};
|
|
1938
|
+
readonly options: {
|
|
1939
|
+
readonly name: "The name of the test account (in your CLI config) to delete";
|
|
1940
|
+
readonly id: "The id of the test account";
|
|
1917
1941
|
};
|
|
1918
|
-
readonly
|
|
1919
|
-
readonly
|
|
1942
|
+
readonly examples: {
|
|
1943
|
+
readonly withPositionalID: (testAccountToDelete: number) => string;
|
|
1944
|
+
readonly withPositionalName: (testAccountToDelete: string) => string;
|
|
1945
|
+
readonly withID: (testAccountToDelete: number) => string;
|
|
1946
|
+
readonly withName: (testAccountToDelete: string) => string;
|
|
1947
|
+
readonly withoutId: "Delete a test account via a prompt";
|
|
1920
1948
|
};
|
|
1921
|
-
readonly example: (testAccountId: number) => string;
|
|
1922
1949
|
};
|
|
1923
1950
|
};
|
|
1924
1951
|
readonly secrets: {
|
|
@@ -2770,6 +2797,7 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
2770
2797
|
readonly compressed: (byteCount: number) => string;
|
|
2771
2798
|
readonly compressing: (path: string) => string;
|
|
2772
2799
|
readonly fileFiltered: (filename: string) => string;
|
|
2800
|
+
readonly legacyFileDetected: (filename: string, platformVersion: string) => string;
|
|
2773
2801
|
};
|
|
2774
2802
|
};
|
|
2775
2803
|
readonly boxen: {
|
|
@@ -2902,11 +2930,12 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
2902
2930
|
readonly setHttpTimeout: {
|
|
2903
2931
|
readonly promptMessage: "Enter http timeout duration";
|
|
2904
2932
|
readonly success: (timeout: string) => string;
|
|
2933
|
+
readonly error: (timeout: string) => string;
|
|
2905
2934
|
};
|
|
2906
2935
|
readonly setAutoOpenBrowser: {
|
|
2907
2936
|
readonly fieldName: "auto open browser";
|
|
2908
|
-
readonly enabled:
|
|
2909
|
-
readonly disabled:
|
|
2937
|
+
readonly enabled: `Successfully updated ${string} to ${string}`;
|
|
2938
|
+
readonly disabled: `Successfully updated ${string} to ${string}`;
|
|
2910
2939
|
};
|
|
2911
2940
|
};
|
|
2912
2941
|
readonly commonOpts: {
|