@hubspot/cli 5.4.0 → 6.0.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/__tests__/projects.test.js +105 -0
- package/commands/accounts/clean.js +1 -1
- package/commands/project/__tests__/deploy.test.js +1 -1
- package/commands/project/__tests__/installDeps.test.js +168 -0
- package/commands/project/__tests__/logs.test.js +305 -0
- package/commands/project/cloneApp.js +3 -16
- package/commands/project/deploy.js +4 -1
- package/commands/project/dev.js +15 -6
- package/commands/project/download.js +4 -1
- package/commands/project/installDeps.js +78 -0
- package/commands/project/logs.js +80 -242
- package/commands/project/migrateApp.js +3 -6
- package/commands/project/upload.js +5 -3
- package/commands/project/watch.js +3 -9
- package/commands/project.js +2 -0
- package/commands/sandbox/create.js +1 -0
- package/commands/sandbox.js +0 -2
- package/lang/en.lyaml +25 -70
- package/lib/LocalDevManager.js +1 -22
- package/lib/__tests__/dependencyManagement.test.js +245 -0
- package/lib/__tests__/projectLogsManager.test.js +210 -0
- package/lib/dependencyManagement.js +157 -0
- package/lib/errorHandlers/apiErrors.js +1 -3
- package/lib/errorHandlers/overrideErrors.js +57 -36
- package/lib/localDev.js +1 -7
- package/lib/projectLogsManager.js +144 -0
- package/lib/projects.js +3 -6
- package/lib/projectsWatch.js +2 -5
- package/lib/prompts/__tests__/projectsLogsPrompt.test.js +46 -0
- package/lib/prompts/createProjectPrompt.js +4 -0
- package/lib/prompts/projectDevTargetAccountPrompt.js +16 -25
- package/lib/prompts/projectsLogsPrompt.js +17 -108
- package/lib/sandboxSync.js +13 -15
- package/package.json +6 -6
- package/commands/sandbox/sync.js +0 -225
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const { logger } = require('@hubspot/local-dev-lib/logger');
|
|
2
|
+
const { getProjectConfig } = require('./projects');
|
|
3
|
+
const { exec: execAsync } = require('child_process');
|
|
4
|
+
const { walk } = require('@hubspot/local-dev-lib/fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { uiLink } = require('./ui');
|
|
7
|
+
const util = require('util');
|
|
8
|
+
const { i18n } = require('./lang');
|
|
9
|
+
const SpinniesManager = require('./ui/SpinniesManager');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
|
|
12
|
+
const DEFAULT_PACKAGE_MANAGER = 'npm';
|
|
13
|
+
|
|
14
|
+
const i18nKey = `commands.project.subcommands.installDeps`;
|
|
15
|
+
|
|
16
|
+
class NoPackageJsonFilesError extends Error {
|
|
17
|
+
constructor(projectName) {
|
|
18
|
+
super(
|
|
19
|
+
i18n(`${i18nKey}.noPackageJsonInProject`, {
|
|
20
|
+
projectName,
|
|
21
|
+
link: uiLink(
|
|
22
|
+
'Learn how to create a project from scratch.',
|
|
23
|
+
'https://developers.hubspot.com/beta-docs/guides/crm/intro/create-a-project'
|
|
24
|
+
),
|
|
25
|
+
})
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function isGloballyInstalled(command) {
|
|
31
|
+
const exec = util.promisify(execAsync);
|
|
32
|
+
try {
|
|
33
|
+
await exec(`${command} --version`);
|
|
34
|
+
return true;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function installPackages({ packages, installLocations }) {
|
|
41
|
+
const installDirs =
|
|
42
|
+
installLocations || (await getProjectPackageJsonLocations());
|
|
43
|
+
await Promise.all(
|
|
44
|
+
installDirs.map(async dir => {
|
|
45
|
+
await installPackagesInDirectory(packages, dir);
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function installPackagesInDirectory(packages, directory) {
|
|
51
|
+
const spinner = `installingDependencies-${directory}`;
|
|
52
|
+
const relativeDir = path.relative(process.cwd(), directory);
|
|
53
|
+
SpinniesManager.init();
|
|
54
|
+
SpinniesManager.add(spinner, {
|
|
55
|
+
text:
|
|
56
|
+
packages && packages.length
|
|
57
|
+
? i18n(`${i18nKey}.addingDependenciesToLocation`, {
|
|
58
|
+
dependencies: `[${packages.join(', ')}]`,
|
|
59
|
+
directory: relativeDir,
|
|
60
|
+
})
|
|
61
|
+
: i18n(`${i18nKey}.installingDependencies`, {
|
|
62
|
+
directory: relativeDir,
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
let installCommand = `${DEFAULT_PACKAGE_MANAGER} --prefix=${directory} install`;
|
|
66
|
+
|
|
67
|
+
if (packages) {
|
|
68
|
+
installCommand = `${installCommand} ${packages.join(' ')}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
logger.debug(`Running ${installCommand}`);
|
|
72
|
+
try {
|
|
73
|
+
const exec = util.promisify(execAsync);
|
|
74
|
+
await exec(installCommand);
|
|
75
|
+
SpinniesManager.succeed(spinner, {
|
|
76
|
+
text: i18n(`${i18nKey}.installationSuccessful`, {
|
|
77
|
+
directory: relativeDir,
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
} catch (e) {
|
|
81
|
+
SpinniesManager.fail(spinner, {
|
|
82
|
+
text: i18n(`${i18nKey}.installingDependenciesFailed`, {
|
|
83
|
+
directory: relativeDir,
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
throw new Error(
|
|
87
|
+
i18n(`${i18nKey}.installingDependenciesFailed`, {
|
|
88
|
+
directory: relativeDir,
|
|
89
|
+
}),
|
|
90
|
+
{
|
|
91
|
+
cause: e,
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function getProjectPackageJsonLocations() {
|
|
98
|
+
const projectConfig = await getProjectConfig();
|
|
99
|
+
|
|
100
|
+
if (
|
|
101
|
+
!projectConfig ||
|
|
102
|
+
!projectConfig.projectDir ||
|
|
103
|
+
!projectConfig.projectConfig
|
|
104
|
+
) {
|
|
105
|
+
throw new Error(i18n(`${i18nKey}.noProjectConfig`));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const {
|
|
109
|
+
projectDir,
|
|
110
|
+
projectConfig: { srcDir, name },
|
|
111
|
+
} = projectConfig;
|
|
112
|
+
|
|
113
|
+
if (!(await isGloballyInstalled(DEFAULT_PACKAGE_MANAGER))) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
i18n(`${i18nKey}.packageManagerNotInstalled`, {
|
|
116
|
+
packageManager: DEFAULT_PACKAGE_MANAGER,
|
|
117
|
+
link: uiLink(
|
|
118
|
+
DEFAULT_PACKAGE_MANAGER,
|
|
119
|
+
'https://docs.npmjs.com/downloading-and-installing-node-js-and-npm'
|
|
120
|
+
),
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (
|
|
126
|
+
!fs.existsSync(projectConfig.projectDir) ||
|
|
127
|
+
!fs.existsSync(path.join(projectDir, srcDir))
|
|
128
|
+
) {
|
|
129
|
+
throw new NoPackageJsonFilesError(name);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const packageJsonFiles = (await walk(path.join(projectDir, srcDir))).filter(
|
|
133
|
+
file =>
|
|
134
|
+
file.includes('package.json') &&
|
|
135
|
+
!file.includes('node_modules') &&
|
|
136
|
+
!file.includes('.vite')
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (packageJsonFiles.length === 0) {
|
|
140
|
+
throw new NoPackageJsonFilesError(name);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const packageParentDirs = [];
|
|
144
|
+
packageJsonFiles.forEach(packageJsonFile => {
|
|
145
|
+
const parentDir = path.dirname(packageJsonFile);
|
|
146
|
+
packageParentDirs.push(parentDir);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return packageParentDirs;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = {
|
|
153
|
+
isGloballyInstalled,
|
|
154
|
+
installPackages,
|
|
155
|
+
DEFAULT_PACKAGE_MANAGER,
|
|
156
|
+
getProjectPackageJsonLocations,
|
|
157
|
+
};
|
|
@@ -28,8 +28,6 @@ class ApiErrorContext extends ErrorContext {
|
|
|
28
28
|
this.request = props.request || '';
|
|
29
29
|
/** @type {string} */
|
|
30
30
|
this.payload = props.payload || '';
|
|
31
|
-
/** @type {string} */
|
|
32
|
-
this.projectName = props.projectName || '';
|
|
33
31
|
}
|
|
34
32
|
}
|
|
35
33
|
|
|
@@ -54,7 +52,7 @@ function logValidationErrors(error, context) {
|
|
|
54
52
|
*/
|
|
55
53
|
function logApiErrorInstance(error, context) {
|
|
56
54
|
if (error.isAxiosError) {
|
|
57
|
-
if (overrideErrors(error)) return;
|
|
55
|
+
if (overrideErrors(error, context)) return;
|
|
58
56
|
const errorWithContext = getAxiosErrorWithContext(error, context);
|
|
59
57
|
logger.error(errorWithContext.message);
|
|
60
58
|
return;
|
|
@@ -1,38 +1,43 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
isSpecifiedError,
|
|
3
|
+
isMissingScopeError,
|
|
4
|
+
} = require('@hubspot/local-dev-lib/errors/apiErrors');
|
|
2
5
|
const { logger } = require('@hubspot/local-dev-lib/logger');
|
|
3
|
-
|
|
4
6
|
const { PLATFORM_VERSION_ERROR_TYPES } = require('../constants');
|
|
5
7
|
const { i18n } = require('../lang');
|
|
6
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
uiAccountDescription,
|
|
10
|
+
uiLine,
|
|
11
|
+
uiLink,
|
|
12
|
+
uiCommandReference,
|
|
13
|
+
} = require('../ui');
|
|
7
14
|
|
|
8
15
|
const i18nKey = 'lib.errorHandlers.overrideErrors';
|
|
9
16
|
|
|
10
|
-
function createPlatformVersionError(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const platformVersionKey = {
|
|
17
|
-
[PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED]:
|
|
18
|
-
'unspecified platformVersion',
|
|
19
|
-
[PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED]:
|
|
20
|
-
errData.context.RETIRED_PLATFORM_VERSION,
|
|
21
|
-
[PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST]:
|
|
22
|
-
errData.context.PLATFORM_VERSION,
|
|
23
|
-
};
|
|
17
|
+
function createPlatformVersionError(err, subCategory) {
|
|
18
|
+
let translationKey = 'unspecifiedPlatformVersion';
|
|
19
|
+
let platformVersion = 'unspecified platformVersion';
|
|
20
|
+
const errorContext =
|
|
21
|
+
err.response && err.response.data && err.response.data.context;
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
[PLATFORM_VERSION_ERROR_TYPES.
|
|
27
|
-
'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
switch (subCategory) {
|
|
24
|
+
case [PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED]:
|
|
25
|
+
translationKey = 'platformVersionRetired';
|
|
26
|
+
if (errorContext && errorContext[subCategory]) {
|
|
27
|
+
platformVersion = errorContext[subCategory];
|
|
28
|
+
}
|
|
29
|
+
break;
|
|
30
|
+
case [
|
|
31
|
+
PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST,
|
|
32
|
+
]:
|
|
33
|
+
translationKey = 'nonExistentPlatformVersion';
|
|
34
|
+
if (errorContext && errorContext[subCategory]) {
|
|
35
|
+
platformVersion = errorContext[subCategory];
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
36
41
|
|
|
37
42
|
uiLine();
|
|
38
43
|
logger.error(i18n(`${i18nKey}.platformVersionErrors.header`));
|
|
@@ -44,21 +49,37 @@ function createPlatformVersionError(subCategory, errData) {
|
|
|
44
49
|
logger.log(i18n(`${i18nKey}.platformVersionErrors.updateProject`));
|
|
45
50
|
logger.log(
|
|
46
51
|
i18n(`${i18nKey}.platformVersionErrors.betaLink`, {
|
|
47
|
-
docsLink
|
|
52
|
+
docsLink: uiLink(
|
|
53
|
+
i18n(`${i18nKey}.platformVersionErrors.docsLink`),
|
|
54
|
+
'https://developers.hubspot.com/docs/platform/platform-versioning'
|
|
55
|
+
),
|
|
48
56
|
})
|
|
49
57
|
);
|
|
50
58
|
uiLine();
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
function overrideErrors(err) {
|
|
61
|
+
function overrideErrors(err, context) {
|
|
62
|
+
if (isMissingScopeError(err)) {
|
|
63
|
+
logger.error(
|
|
64
|
+
i18n(`${i18nKey}.missingScopeError`, {
|
|
65
|
+
accountName: context.accountId
|
|
66
|
+
? uiAccountDescription(context.accountId)
|
|
67
|
+
: '',
|
|
68
|
+
request: context.request || 'request',
|
|
69
|
+
authCommand: uiCommandReference('hs auth'),
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
54
75
|
if (
|
|
55
76
|
isSpecifiedError(err, {
|
|
56
77
|
subCategory: PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED,
|
|
57
78
|
})
|
|
58
79
|
) {
|
|
59
80
|
createPlatformVersionError(
|
|
60
|
-
|
|
61
|
-
|
|
81
|
+
err,
|
|
82
|
+
PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_NOT_SPECIFIED
|
|
62
83
|
);
|
|
63
84
|
return true;
|
|
64
85
|
}
|
|
@@ -69,8 +90,8 @@ function overrideErrors(err) {
|
|
|
69
90
|
})
|
|
70
91
|
) {
|
|
71
92
|
createPlatformVersionError(
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
err,
|
|
94
|
+
PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_RETIRED
|
|
74
95
|
);
|
|
75
96
|
return true;
|
|
76
97
|
}
|
|
@@ -82,8 +103,8 @@ function overrideErrors(err) {
|
|
|
82
103
|
})
|
|
83
104
|
) {
|
|
84
105
|
createPlatformVersionError(
|
|
85
|
-
|
|
86
|
-
|
|
106
|
+
err,
|
|
107
|
+
PLATFORM_VERSION_ERROR_TYPES.PLATFORM_VERSION_SPECIFIED_DOES_NOT_EXIST
|
|
87
108
|
);
|
|
88
109
|
return true;
|
|
89
110
|
}
|
package/lib/localDev.js
CHANGED
|
@@ -129,13 +129,6 @@ const suggestRecommendedNestedAccount = async (
|
|
|
129
129
|
`${i18nKey}.validateAccountOption.publicAppNonDeveloperTestAccountWarning`
|
|
130
130
|
)
|
|
131
131
|
);
|
|
132
|
-
} else if (isAppDeveloperAccount(accountConfig)) {
|
|
133
|
-
logger.error(
|
|
134
|
-
i18n(
|
|
135
|
-
`${i18nKey}.validateAccountOption.privateAppInAppDeveloperAccountError`
|
|
136
|
-
)
|
|
137
|
-
);
|
|
138
|
-
process.exit(EXIT_CODES.ERROR);
|
|
139
132
|
} else {
|
|
140
133
|
logger.log(i18n(`${i18nKey}.validateAccountOption.nonSandboxWarning`));
|
|
141
134
|
}
|
|
@@ -202,6 +195,7 @@ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
|
|
|
202
195
|
accountConfig,
|
|
203
196
|
sandboxAccountConfig
|
|
204
197
|
);
|
|
198
|
+
// For v1 sandboxes, keep sync here. Once we migrate to v2, this will be handled by BE automatically
|
|
205
199
|
await syncSandbox({
|
|
206
200
|
accountConfig: sandboxAccountConfig,
|
|
207
201
|
parentAccountConfig: accountConfig,
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const { getProjectConfig, ensureProjectExists } = require('./projects');
|
|
2
|
+
const {
|
|
3
|
+
fetchProjectComponentsMetadata,
|
|
4
|
+
} = require('@hubspot/local-dev-lib/api/projects');
|
|
5
|
+
const { i18n } = require('./lang');
|
|
6
|
+
const { uiLink } = require('./ui');
|
|
7
|
+
|
|
8
|
+
const i18nKey = 'commands.project.subcommands.logs';
|
|
9
|
+
|
|
10
|
+
class ProjectLogsManager {
|
|
11
|
+
reset() {
|
|
12
|
+
Object.keys(this).forEach(key => {
|
|
13
|
+
if (Object.hasOwn(this, key)) {
|
|
14
|
+
this[key] = undefined;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async init(accountId) {
|
|
20
|
+
const { projectConfig } = await getProjectConfig();
|
|
21
|
+
|
|
22
|
+
if (!projectConfig || !projectConfig.name) {
|
|
23
|
+
throw new Error(i18n(`${i18nKey}.errors.noProjectConfig`));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { name: projectName } = projectConfig;
|
|
27
|
+
|
|
28
|
+
this.projectName = projectName;
|
|
29
|
+
this.accountId = accountId;
|
|
30
|
+
this.functions = [];
|
|
31
|
+
|
|
32
|
+
const { project } = await ensureProjectExists(
|
|
33
|
+
this.accountId,
|
|
34
|
+
this.projectName,
|
|
35
|
+
{
|
|
36
|
+
allowCreate: false,
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (
|
|
41
|
+
!project ||
|
|
42
|
+
!project.deployedBuild ||
|
|
43
|
+
!project.deployedBuild.subbuildStatuses
|
|
44
|
+
) {
|
|
45
|
+
throw new Error(i18n(`${i18nKey}.errors.failedToFetchProjectDetails`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.projectId = project.id;
|
|
49
|
+
await this.fetchFunctionDetails();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async fetchFunctionDetails() {
|
|
53
|
+
if (!this.projectId) {
|
|
54
|
+
throw new Error(i18n(`${i18nKey}.errors.noProjectConfig`));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const { topLevelComponentMetadata } = await fetchProjectComponentsMetadata(
|
|
58
|
+
this.accountId,
|
|
59
|
+
this.projectId
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const apps = topLevelComponentMetadata.filter(componentMetadata => {
|
|
63
|
+
const { type } = componentMetadata;
|
|
64
|
+
return type && type.name === 'PRIVATE_APP';
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!this.functions) {
|
|
68
|
+
this.functions = [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
apps.forEach(app => {
|
|
72
|
+
this.functions.push(
|
|
73
|
+
...app.featureComponents.filter(
|
|
74
|
+
component => component.type.name === 'APP_FUNCTION'
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (this.functions.length === 0) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
i18n(`${i18nKey}.errors.noFunctionsInProject`, {
|
|
82
|
+
link: uiLink(
|
|
83
|
+
i18n(`${i18nKey}.errors.noFunctionsLinkText`),
|
|
84
|
+
'https://developers.hubspot.com/docs/platform/serverless-functions'
|
|
85
|
+
),
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getFunctionNames() {
|
|
92
|
+
if (!this.functions) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
return this.functions.map(
|
|
96
|
+
serverlessFunction => serverlessFunction.componentName
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setFunction(functionName) {
|
|
101
|
+
if (!this.functions) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
i18n(`${i18nKey}.errors.noFunctionsInProject`, {
|
|
104
|
+
link: uiLink(
|
|
105
|
+
i18n(`${i18nKey}.errors.noFunctionsLinkText`),
|
|
106
|
+
'https://developers.hubspot.com/docs/platform/serverless-functions'
|
|
107
|
+
),
|
|
108
|
+
}),
|
|
109
|
+
{
|
|
110
|
+
projectName: this.projectName,
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.selectedFunction = this.functions.find(
|
|
116
|
+
serverlessFunction => serverlessFunction.componentName === functionName
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (!this.selectedFunction) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
i18n(`${i18nKey}.errors.noFunctionWithName`, { name: functionName })
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.functionName = functionName;
|
|
126
|
+
|
|
127
|
+
if (!this.selectedFunction.deployOutput) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
i18n(`${i18nKey}.errors.functionNotDeployed`, { name: functionName })
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
this.appId = this.selectedFunction.deployOutput.appId;
|
|
133
|
+
|
|
134
|
+
if (this.selectedFunction.deployOutput.endpoint) {
|
|
135
|
+
this.endpointName = this.selectedFunction.deployOutput.endpoint.path;
|
|
136
|
+
this.method = this.selectedFunction.deployOutput.endpoint.method;
|
|
137
|
+
this.isPublicFunction = true;
|
|
138
|
+
} else {
|
|
139
|
+
this.isPublicFunction = false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = new ProjectLogsManager();
|
package/lib/projects.js
CHANGED
|
@@ -287,10 +287,7 @@ const ensureProjectExists = async (
|
|
|
287
287
|
);
|
|
288
288
|
return { projectExists: true, project };
|
|
289
289
|
} catch (err) {
|
|
290
|
-
return logApiErrorInstance(
|
|
291
|
-
err,
|
|
292
|
-
new ApiErrorContext({ accountId, projectName })
|
|
293
|
-
);
|
|
290
|
+
return logApiErrorInstance(err, new ApiErrorContext({ accountId }));
|
|
294
291
|
}
|
|
295
292
|
} else {
|
|
296
293
|
if (!noLogs) {
|
|
@@ -312,7 +309,7 @@ const ensureProjectExists = async (
|
|
|
312
309
|
logger.error(err.message);
|
|
313
310
|
process.exit(EXIT_CODES.ERROR);
|
|
314
311
|
}
|
|
315
|
-
logApiErrorInstance(err, new ApiErrorContext({ accountId
|
|
312
|
+
logApiErrorInstance(err, new ApiErrorContext({ accountId }));
|
|
316
313
|
process.exit(EXIT_CODES.ERROR);
|
|
317
314
|
}
|
|
318
315
|
};
|
|
@@ -572,7 +569,7 @@ const handleProjectUpload = async (
|
|
|
572
569
|
buildId
|
|
573
570
|
);
|
|
574
571
|
}
|
|
575
|
-
resolve(uploadResult);
|
|
572
|
+
resolve(uploadResult || {});
|
|
576
573
|
})
|
|
577
574
|
);
|
|
578
575
|
|
package/lib/projectsWatch.js
CHANGED
|
@@ -85,10 +85,7 @@ const debounceQueueBuild = (accountId, projectName, platformVersion) => {
|
|
|
85
85
|
logger.log(i18n(`${i18nKey}.logs.watchCancelledFromUi`));
|
|
86
86
|
process.exit(0);
|
|
87
87
|
} else {
|
|
88
|
-
logApiErrorInstance(
|
|
89
|
-
err,
|
|
90
|
-
new ApiErrorContext({ accountId, projectName })
|
|
91
|
-
);
|
|
88
|
+
logApiErrorInstance(err, new ApiErrorContext({ accountId }));
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
return;
|
|
@@ -158,7 +155,7 @@ const createNewBuild = async (accountId, projectName, platformVersion) => {
|
|
|
158
155
|
);
|
|
159
156
|
return buildId;
|
|
160
157
|
} catch (err) {
|
|
161
|
-
logApiErrorInstance(err, new ApiErrorContext({ accountId
|
|
158
|
+
logApiErrorInstance(err, new ApiErrorContext({ accountId }));
|
|
162
159
|
if (
|
|
163
160
|
isSpecifiedError(err, { subCategory: PROJECT_ERROR_TYPES.PROJECT_LOCKED })
|
|
164
161
|
) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const { projectLogsPrompt } = require('../projectsLogsPrompt');
|
|
2
|
+
|
|
3
|
+
jest.mock('../promptUtils');
|
|
4
|
+
const { promptUser } = require('../promptUtils');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
describe('prompts/projectsLogsPrompt', () => {
|
|
8
|
+
it('should return undefined functionName when functionChoices is nullable', async () => {
|
|
9
|
+
const actual = await projectLogsPrompt({ functionChoices: null });
|
|
10
|
+
expect(actual).toEqual({});
|
|
11
|
+
expect(promptUser).not.toHaveBeenCalled();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return the functionName without prompting when there is only one functionChoice', async () => {
|
|
15
|
+
const functionChoice = 'this-is-the-only-function';
|
|
16
|
+
const { functionName } = await projectLogsPrompt({
|
|
17
|
+
functionChoices: [functionChoice],
|
|
18
|
+
});
|
|
19
|
+
expect(functionName).toEqual(functionChoice);
|
|
20
|
+
expect(promptUser).not.toHaveBeenCalled();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should prompt the user if there is more than one choice', async () => {
|
|
24
|
+
const functionChoices = ['choice 1', 'choice 2'];
|
|
25
|
+
const projectName = 'my cool project';
|
|
26
|
+
await projectLogsPrompt({
|
|
27
|
+
functionChoices,
|
|
28
|
+
projectName,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(promptUser).toHaveBeenCalledTimes(1);
|
|
32
|
+
expect(promptUser).toHaveBeenLastCalledWith(
|
|
33
|
+
expect.arrayContaining([
|
|
34
|
+
expect.objectContaining({
|
|
35
|
+
name: 'functionName',
|
|
36
|
+
type: 'list',
|
|
37
|
+
message: `[--function] Select function in ${chalk.bold(
|
|
38
|
+
projectName
|
|
39
|
+
)} project`,
|
|
40
|
+
when: expect.any(Function),
|
|
41
|
+
choices: functionChoices,
|
|
42
|
+
}),
|
|
43
|
+
])
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -4,6 +4,7 @@ const {
|
|
|
4
4
|
getCwd,
|
|
5
5
|
sanitizeFileName,
|
|
6
6
|
isValidPath,
|
|
7
|
+
untildify,
|
|
7
8
|
} = require('@hubspot/local-dev-lib/path');
|
|
8
9
|
const { PROJECT_COMPONENT_TYPES } = require('../../lib/constants');
|
|
9
10
|
const { promptUser } = require('./promptUtils');
|
|
@@ -100,6 +101,9 @@ const createProjectPrompt = async (
|
|
|
100
101
|
}
|
|
101
102
|
return true;
|
|
102
103
|
},
|
|
104
|
+
filter: input => {
|
|
105
|
+
return untildify(input);
|
|
106
|
+
},
|
|
103
107
|
},
|
|
104
108
|
{
|
|
105
109
|
name: 'template',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { promptUser } = require('./promptUtils');
|
|
2
2
|
const { i18n } = require('../lang');
|
|
3
3
|
const { uiAccountDescription, uiCommandReference } = require('../ui');
|
|
4
|
-
const { isSandbox
|
|
4
|
+
const { isSandbox } = require('../accountTypes');
|
|
5
5
|
const { getAccountId } = require('@hubspot/local-dev-lib/config');
|
|
6
6
|
const { getSandboxUsageLimits } = require('@hubspot/local-dev-lib/sandboxes');
|
|
7
7
|
const {
|
|
@@ -99,7 +99,6 @@ const selectDeveloperTestTargetAccountPrompt = async (
|
|
|
99
99
|
defaultAccountConfig
|
|
100
100
|
) => {
|
|
101
101
|
const defaultAccountId = getAccountId(defaultAccountConfig.name);
|
|
102
|
-
let choices = [];
|
|
103
102
|
let devTestAccountsResponse = undefined;
|
|
104
103
|
try {
|
|
105
104
|
devTestAccountsResponse = await fetchDeveloperTestAccounts(
|
|
@@ -109,13 +108,6 @@ const selectDeveloperTestTargetAccountPrompt = async (
|
|
|
109
108
|
logger.debug('Unable to fetch developer test account usage limits: ', err);
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
const devTestAccounts = accounts
|
|
113
|
-
.reverse()
|
|
114
|
-
.filter(
|
|
115
|
-
config =>
|
|
116
|
-
isDeveloperTestAccount(config) &&
|
|
117
|
-
config.parentAccountId === defaultAccountId
|
|
118
|
-
);
|
|
119
111
|
let disabledMessage = false;
|
|
120
112
|
if (
|
|
121
113
|
devTestAccountsResponse &&
|
|
@@ -128,27 +120,26 @@ const selectDeveloperTestTargetAccountPrompt = async (
|
|
|
128
120
|
});
|
|
129
121
|
}
|
|
130
122
|
|
|
131
|
-
|
|
123
|
+
const devTestAccounts = [];
|
|
132
124
|
if (devTestAccountsResponse && devTestAccountsResponse.results) {
|
|
133
|
-
const
|
|
125
|
+
const accountIds = accounts.map(account => account.portalId);
|
|
126
|
+
|
|
134
127
|
devTestAccountsResponse.results.forEach(acct => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
128
|
+
const inConfig = accountIds.includes(acct.id);
|
|
129
|
+
devTestAccounts.push({
|
|
130
|
+
name: getNonConfigDeveloperTestAccountName(acct),
|
|
131
|
+
value: {
|
|
132
|
+
targetAccountId: acct.id,
|
|
133
|
+
createdNestedAccount: false,
|
|
134
|
+
parentAccountId: defaultAccountId,
|
|
135
|
+
notInConfigAccount: inConfig ? null : acct,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
146
138
|
});
|
|
147
139
|
}
|
|
148
140
|
|
|
149
|
-
choices = [
|
|
150
|
-
...devTestAccounts
|
|
151
|
-
...devTestAccountsNotInConfig,
|
|
141
|
+
const choices = [
|
|
142
|
+
...devTestAccounts,
|
|
152
143
|
{
|
|
153
144
|
name: i18n(`${i18nKey}.createNewDeveloperTestAccountOption`),
|
|
154
145
|
value: {
|