@hubspot/cli 7.7.27-experimental.1 → 7.7.28-experimental.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/README.md +0 -4
- package/api/__tests__/migrate.test.js +5 -5
- package/api/migrate.d.ts +10 -4
- package/api/migrate.js +2 -2
- package/commands/__tests__/create.test.js +20 -0
- package/commands/__tests__/testAccount.test.js +2 -0
- package/commands/app/__tests__/migrate.test.js +1 -0
- package/commands/create/function.js +2 -2
- package/commands/create/module.js +2 -2
- package/commands/create/template.js +2 -2
- package/commands/create.js +47 -0
- package/commands/getStarted.js +66 -4
- package/commands/mcp/setup.d.ts +0 -1
- package/commands/mcp/setup.js +3 -11
- package/commands/project/__tests__/create.test.js +57 -0
- package/commands/project/__tests__/devUnifiedFlow.test.js +18 -30
- package/commands/project/create.js +6 -1
- package/commands/project/deploy.js +31 -1
- package/commands/project/dev/deprecatedFlow.js +2 -1
- package/commands/project/dev/index.js +32 -12
- package/commands/project/dev/unifiedFlow.d.ts +1 -1
- package/commands/project/dev/unifiedFlow.js +10 -16
- package/commands/project/profile/delete.js +26 -14
- package/commands/project/upload.d.ts +2 -2
- package/commands/project/upload.js +1 -1
- package/commands/testAccount/__tests__/importData.test.d.ts +1 -0
- package/commands/testAccount/__tests__/importData.test.js +93 -0
- package/commands/testAccount/create.js +23 -13
- package/commands/testAccount/importData.d.ts +9 -0
- package/commands/testAccount/importData.js +61 -0
- package/commands/testAccount.js +2 -0
- package/lang/en.d.ts +160 -46
- package/lang/en.js +175 -59
- package/lang/en.lyaml +35 -14
- package/lib/__tests__/importData.test.d.ts +1 -0
- package/lib/__tests__/importData.test.js +89 -0
- package/lib/accountTypes.js +2 -3
- package/lib/app/__tests__/migrate.test.js +81 -36
- package/lib/app/migrate.d.ts +17 -4
- package/lib/app/migrate.js +97 -19
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +1 -0
- package/lib/hasFeature.d.ts +1 -0
- package/lib/hasFeature.js +7 -0
- package/lib/importData.d.ts +3 -0
- package/lib/importData.js +50 -0
- package/lib/mcp/setup.d.ts +0 -2
- package/lib/mcp/setup.js +0 -24
- package/lib/process.js +15 -4
- package/lib/projectProfiles.d.ts +1 -1
- package/lib/projectProfiles.js +10 -2
- package/lib/projects/__tests__/AppDevModeInterface.test.js +3 -3
- package/lib/projects/__tests__/LocalDevProcess.test.js +5 -95
- package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +6 -6
- package/lib/projects/__tests__/components.test.js +164 -7
- package/lib/projects/__tests__/localDevProjectHelpers.test.d.ts +1 -0
- package/lib/projects/__tests__/localDevProjectHelpers.test.js +118 -0
- package/lib/projects/add/v3AddComponent.js +16 -4
- package/lib/projects/components.d.ts +1 -0
- package/lib/projects/components.js +27 -1
- package/lib/projects/localDev/AppDevModeInterface.js +35 -3
- package/lib/projects/localDev/LocalDevLogger.d.ts +0 -4
- package/lib/projects/localDev/LocalDevLogger.js +2 -19
- package/lib/projects/localDev/LocalDevManager.js +1 -1
- package/lib/projects/localDev/LocalDevProcess.d.ts +1 -2
- package/lib/projects/localDev/LocalDevProcess.js +3 -26
- package/lib/projects/localDev/LocalDevState.d.ts +6 -7
- package/lib/projects/localDev/LocalDevState.js +16 -15
- package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +1 -0
- package/lib/projects/localDev/LocalDevWebsocketServer.js +17 -2
- package/lib/projects/localDev/{helpers.d.ts → helpers/account.d.ts} +1 -7
- package/lib/projects/localDev/{helpers.js → helpers/account.js} +44 -144
- package/lib/projects/localDev/helpers/project.d.ts +12 -0
- package/lib/projects/localDev/helpers/project.js +173 -0
- package/lib/projects/urls.d.ts +1 -0
- package/lib/projects/urls.js +4 -0
- package/lib/prompts/__tests__/createFunctionPrompt.test.d.ts +1 -0
- package/lib/prompts/__tests__/createFunctionPrompt.test.js +129 -0
- package/lib/prompts/__tests__/createModulePrompt.test.d.ts +1 -0
- package/lib/prompts/__tests__/createModulePrompt.test.js +187 -0
- package/lib/prompts/__tests__/createTemplatePrompt.test.d.ts +1 -0
- package/lib/prompts/__tests__/createTemplatePrompt.test.js +102 -0
- package/lib/prompts/confirmImportDataPrompt.d.ts +1 -0
- package/lib/prompts/confirmImportDataPrompt.js +12 -0
- package/lib/prompts/createFunctionPrompt.d.ts +2 -1
- package/lib/prompts/createFunctionPrompt.js +36 -7
- package/lib/prompts/createModulePrompt.d.ts +2 -1
- package/lib/prompts/createModulePrompt.js +48 -1
- package/lib/prompts/createTemplatePrompt.d.ts +3 -24
- package/lib/prompts/createTemplatePrompt.js +9 -1
- package/lib/prompts/importDataFilePathPrompt.d.ts +1 -0
- package/lib/prompts/importDataFilePathPrompt.js +24 -0
- package/lib/prompts/importDataTestAccountSelectPrompt.d.ts +3 -0
- package/lib/prompts/importDataTestAccountSelectPrompt.js +29 -0
- package/lib/prompts/projectDevTargetAccountPrompt.js +1 -0
- package/lib/prompts/promptUtils.d.ts +7 -1
- package/lib/prompts/promptUtils.js +14 -1
- package/lib/ui/__tests__/removeAnsiCodes.test.d.ts +1 -0
- package/lib/ui/__tests__/removeAnsiCodes.test.js +84 -0
- package/lib/ui/index.js +3 -6
- package/lib/ui/removeAnsiCodes.d.ts +1 -0
- package/lib/ui/removeAnsiCodes.js +4 -0
- package/mcp-server/server.js +2 -1
- package/mcp-server/tools/cms/HsListTool.d.ts +23 -0
- package/mcp-server/tools/cms/HsListTool.js +58 -0
- package/mcp-server/tools/cms/__tests__/HsListTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsListTool.test.js +120 -0
- package/mcp-server/tools/index.d.ts +1 -0
- package/mcp-server/tools/index.js +8 -0
- package/mcp-server/tools/project/DocFetchTool.d.ts +17 -0
- package/mcp-server/tools/project/DocFetchTool.js +49 -0
- package/mcp-server/tools/project/DocsSearchTool.d.ts +26 -0
- package/mcp-server/tools/project/DocsSearchTool.js +62 -0
- package/mcp-server/tools/project/GetConfigValuesTool.js +3 -2
- package/mcp-server/tools/project/__tests__/DocFetchTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +117 -0
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +190 -0
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +1 -1
- package/mcp-server/tools/project/constants.d.ts +2 -0
- package/mcp-server/tools/project/constants.js +6 -0
- package/mcp-server/utils/toolUsageTracking.d.ts +3 -1
- package/mcp-server/utils/toolUsageTracking.js +2 -1
- package/package.json +9 -6
- package/types/Cms.d.ts +16 -0
- package/types/Cms.js +25 -1
- package/types/LocalDev.d.ts +0 -3
- package/types/Prompts.d.ts +1 -0
- package/types/Yargs.d.ts +1 -1
- package/ui/index.d.ts +1 -0
- package/ui/index.js +6 -0
|
@@ -1,41 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { HUBSPOT_ACCOUNT_TYPE_STRINGS } from '@hubspot/local-dev-lib/constants/config';
|
|
2
|
+
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
|
|
3
|
+
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
4
|
+
import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
|
|
3
5
|
import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
|
|
4
|
-
import {
|
|
5
|
-
import { createProject } from '@hubspot/local-dev-lib/api/projects';
|
|
6
|
-
import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
|
|
6
|
+
import { isMissingScopeError } from '@hubspot/local-dev-lib/errors/index';
|
|
7
7
|
import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constants/auth';
|
|
8
|
-
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
9
8
|
import { getSandboxUsageLimits } from '@hubspot/local-dev-lib/api/sandboxHubs';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { hubspotAccountNamePrompt } from '
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
9
|
+
import { uiLogger } from '../../../ui/logger.js';
|
|
10
|
+
import { lib } from '../../../../lang/en.js';
|
|
11
|
+
import { EXIT_CODES } from '../../../enums/exitCodes.js';
|
|
12
|
+
import { confirmDefaultAccountPrompt } from '../../../prompts/projectDevTargetAccountPrompt.js';
|
|
13
|
+
import { isUnifiedAccount } from '../../../accountTypes.js';
|
|
14
|
+
import { isAppDeveloperAccount } from '../../../accountTypes.js';
|
|
15
|
+
import { isDeveloperTestAccount } from '../../../accountTypes.js';
|
|
16
|
+
import { uiAccountDescription } from '../../../ui/index.js';
|
|
17
|
+
import { uiLine } from '../../../ui/index.js';
|
|
18
|
+
import { selectDeveloperTestTargetAccountPrompt } from '../../../prompts/projectDevTargetAccountPrompt.js';
|
|
19
|
+
import { selectSandboxTargetAccountPrompt } from '../../../prompts/projectDevTargetAccountPrompt.js';
|
|
20
|
+
import { validateSandboxUsageLimits } from '../../../sandboxes.js';
|
|
21
|
+
import { logError } from '../../../errorHandlers/index.js';
|
|
22
|
+
import { syncSandbox } from '../../../sandboxSync.js';
|
|
23
|
+
import { getAvailableSyncTypes } from '../../../sandboxes.js';
|
|
24
|
+
import { hubspotAccountNamePrompt } from '../../../prompts/accountNamePrompt.js';
|
|
25
|
+
import { trackCommandMetadataUsage } from '../../../usageTracking.js';
|
|
26
|
+
import { validateDevTestAccountUsageLimits } from '../../../developerTestAccounts.js';
|
|
27
|
+
import { buildSandbox, buildDeveloperTestAccount, saveAccountToConfig, } from '../../../buildAccount.js';
|
|
28
|
+
import { debugError } from '../../../errorHandlers/index.js';
|
|
29
|
+
import { listPrompt } from '../../../prompts/promptUtils.js';
|
|
30
|
+
import { confirmUseExistingDeveloperTestAccountPrompt } from '../../../prompts/projectDevTargetAccountPrompt.js';
|
|
28
31
|
// If the user passed in the --account flag, confirm they want to use that account as
|
|
29
32
|
// their target account, otherwise exit
|
|
30
33
|
export async function confirmDefaultAccountIsTarget(accountConfig) {
|
|
31
34
|
if (!accountConfig.name || !accountConfig.accountType) {
|
|
32
|
-
uiLogger.error(lib.localDevHelpers.confirmDefaultAccountIsTarget.configError);
|
|
35
|
+
uiLogger.error(lib.localDevHelpers.account.confirmDefaultAccountIsTarget.configError);
|
|
33
36
|
process.exit(EXIT_CODES.ERROR);
|
|
34
37
|
}
|
|
35
38
|
uiLogger.log('');
|
|
36
39
|
const useDefaultAccount = await confirmDefaultAccountPrompt(accountConfig.name, HUBSPOT_ACCOUNT_TYPE_STRINGS[accountConfig.accountType]);
|
|
37
40
|
if (!useDefaultAccount) {
|
|
38
|
-
uiLogger.log(lib.localDevHelpers.confirmDefaultAccountIsTarget
|
|
41
|
+
uiLogger.log(lib.localDevHelpers.account.confirmDefaultAccountIsTarget
|
|
39
42
|
.declineDefaultAccountExplanation);
|
|
40
43
|
process.exit(EXIT_CODES.SUCCESS);
|
|
41
44
|
}
|
|
@@ -47,18 +50,18 @@ export async function checkIfDefaultAccountIsSupported(accountConfig, hasPublicA
|
|
|
47
50
|
!(isAppDeveloperAccount(accountConfig) ||
|
|
48
51
|
isDeveloperTestAccount(accountConfig) ||
|
|
49
52
|
defaultAccountIsUnified)) {
|
|
50
|
-
uiLogger.error(lib.localDevHelpers.checkIfDefaultAccountIsSupported.publicApp);
|
|
53
|
+
uiLogger.error(lib.localDevHelpers.account.checkIfDefaultAccountIsSupported.publicApp);
|
|
51
54
|
process.exit(EXIT_CODES.SUCCESS);
|
|
52
55
|
}
|
|
53
56
|
else if (!hasPublicApps && isAppDeveloperAccount(accountConfig)) {
|
|
54
|
-
uiLogger.error(lib.localDevHelpers.checkIfDefaultAccountIsSupported.privateApp);
|
|
57
|
+
uiLogger.error(lib.localDevHelpers.account.checkIfDefaultAccountIsSupported.privateApp);
|
|
55
58
|
process.exit(EXIT_CODES.SUCCESS);
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
export function checkIfParentAccountIsAuthed(accountConfig) {
|
|
59
62
|
if (!accountConfig.parentAccountId ||
|
|
60
63
|
!getAccountConfig(accountConfig.parentAccountId)) {
|
|
61
|
-
uiLogger.error(lib.localDevHelpers.checkIfParentAccountIsAuthed.notAuthedError(accountConfig.parentAccountId || '', uiAccountDescription(getAccountIdentifier(accountConfig))));
|
|
64
|
+
uiLogger.error(lib.localDevHelpers.account.checkIfParentAccountIsAuthed.notAuthedError(accountConfig.parentAccountId || '', uiAccountDescription(getAccountIdentifier(accountConfig))));
|
|
62
65
|
process.exit(EXIT_CODES.SUCCESS);
|
|
63
66
|
}
|
|
64
67
|
}
|
|
@@ -66,13 +69,14 @@ export function checkIfParentAccountIsAuthed(accountConfig) {
|
|
|
66
69
|
export function checkIfAccountFlagIsSupported(accountConfig, hasPublicApps) {
|
|
67
70
|
if (hasPublicApps) {
|
|
68
71
|
if (!isDeveloperTestAccount(accountConfig)) {
|
|
69
|
-
uiLogger.error(lib.localDevHelpers.validateAccountOption
|
|
72
|
+
uiLogger.error(lib.localDevHelpers.account.validateAccountOption
|
|
73
|
+
.invalidPublicAppAccount);
|
|
70
74
|
process.exit(EXIT_CODES.SUCCESS);
|
|
71
75
|
}
|
|
72
76
|
checkIfParentAccountIsAuthed(accountConfig);
|
|
73
77
|
}
|
|
74
78
|
else if (isAppDeveloperAccount(accountConfig)) {
|
|
75
|
-
uiLogger.error(lib.localDevHelpers.validateAccountOption.invalidPrivateAppAccount);
|
|
79
|
+
uiLogger.error(lib.localDevHelpers.account.validateAccountOption.invalidPrivateAppAccount);
|
|
76
80
|
process.exit(EXIT_CODES.SUCCESS);
|
|
77
81
|
}
|
|
78
82
|
}
|
|
@@ -81,11 +85,11 @@ export async function suggestRecommendedNestedAccount(accounts, accountConfig, h
|
|
|
81
85
|
uiLogger.log('');
|
|
82
86
|
uiLine();
|
|
83
87
|
if (hasPublicApps) {
|
|
84
|
-
uiLogger.log(lib.localDevHelpers.validateAccountOption
|
|
88
|
+
uiLogger.log(lib.localDevHelpers.account.validateAccountOption
|
|
85
89
|
.publicAppNonDeveloperTestAccountWarning);
|
|
86
90
|
}
|
|
87
91
|
else {
|
|
88
|
-
uiLogger.log(lib.localDevHelpers.validateAccountOption.nonSandboxWarning);
|
|
92
|
+
uiLogger.log(lib.localDevHelpers.account.validateAccountOption.nonSandboxWarning);
|
|
89
93
|
}
|
|
90
94
|
uiLine();
|
|
91
95
|
uiLogger.log('');
|
|
@@ -177,7 +181,7 @@ export async function useExistingDevTestAccount(env, account) {
|
|
|
177
181
|
const useExistingDevTestAcct = await confirmUseExistingDeveloperTestAccountPrompt(account);
|
|
178
182
|
if (!useExistingDevTestAcct) {
|
|
179
183
|
uiLogger.log('');
|
|
180
|
-
uiLogger.log(lib.localDevHelpers.confirmDefaultAccountIsTarget
|
|
184
|
+
uiLogger.log(lib.localDevHelpers.account.confirmDefaultAccountIsTarget
|
|
181
185
|
.declineDefaultAccountExplanation);
|
|
182
186
|
uiLogger.log('');
|
|
183
187
|
process.exit(EXIT_CODES.SUCCESS);
|
|
@@ -185,110 +189,6 @@ export async function useExistingDevTestAccount(env, account) {
|
|
|
185
189
|
const devTestAcctConfigName = await saveAccountToConfig(account.id, account.accountName, env);
|
|
186
190
|
uiLogger.success(lib.developerTestAccount.create.success.configFileUpdated(devTestAcctConfigName, PERSONAL_ACCESS_KEY_AUTH_METHOD.name));
|
|
187
191
|
}
|
|
188
|
-
// Prompt the user to create a new project if one doesn't exist on their target account
|
|
189
|
-
export async function createNewProjectForLocalDev(projectConfig, targetAccountId, shouldCreateWithoutConfirmation, hasPublicApps) {
|
|
190
|
-
// Create the project without prompting if this is a newly created sandbox
|
|
191
|
-
let shouldCreateProject = shouldCreateWithoutConfirmation;
|
|
192
|
-
if (!shouldCreateProject) {
|
|
193
|
-
const explanationLangFunction = hasPublicApps
|
|
194
|
-
? lib.localDevHelpers.createNewProjectForLocalDev
|
|
195
|
-
.publicAppProjectMustExistExplanation
|
|
196
|
-
: lib.localDevHelpers.createNewProjectForLocalDev
|
|
197
|
-
.projectMustExistExplanation;
|
|
198
|
-
const explanationString = explanationLangFunction(projectConfig.name, targetAccountId);
|
|
199
|
-
uiLogger.log('');
|
|
200
|
-
uiLine();
|
|
201
|
-
uiLogger.log(explanationString);
|
|
202
|
-
uiLine();
|
|
203
|
-
shouldCreateProject = await confirmPrompt(lib.localDevHelpers.createNewProjectForLocalDev.createProject(projectConfig.name, uiAccountDescription(targetAccountId)));
|
|
204
|
-
}
|
|
205
|
-
if (shouldCreateProject) {
|
|
206
|
-
SpinniesManager.add('createProject', {
|
|
207
|
-
text: lib.localDevHelpers.createNewProjectForLocalDev.creatingProject(projectConfig.name, uiAccountDescription(targetAccountId)),
|
|
208
|
-
});
|
|
209
|
-
try {
|
|
210
|
-
const { data: project } = await createProject(targetAccountId, projectConfig.name);
|
|
211
|
-
SpinniesManager.succeed('createProject', {
|
|
212
|
-
text: lib.localDevHelpers.createNewProjectForLocalDev.createdProject(projectConfig.name, uiAccountDescription(targetAccountId)),
|
|
213
|
-
succeedColor: 'white',
|
|
214
|
-
});
|
|
215
|
-
return project;
|
|
216
|
-
}
|
|
217
|
-
catch (err) {
|
|
218
|
-
SpinniesManager.fail('createProject');
|
|
219
|
-
uiLogger.log(lib.localDevHelpers.createNewProjectForLocalDev.failedToCreateProject);
|
|
220
|
-
process.exit(EXIT_CODES.ERROR);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
// We cannot continue if the project does not exist in the target account
|
|
225
|
-
uiLogger.log('');
|
|
226
|
-
uiLogger.log(lib.localDevHelpers.createNewProjectForLocalDev.choseNotToCreateProject);
|
|
227
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function projectUploadCallback(accountId, projectConfig, tempFile, buildId) {
|
|
231
|
-
if (!buildId) {
|
|
232
|
-
uiLogger.error(lib.localDevHelpers.createInitialBuildForNewProject.genericError);
|
|
233
|
-
process.exit(EXIT_CODES.ERROR);
|
|
234
|
-
}
|
|
235
|
-
return pollProjectBuildAndDeploy(accountId, projectConfig, tempFile, buildId, true);
|
|
236
|
-
}
|
|
237
|
-
// Create an initial build if the project was newly created in the account
|
|
238
|
-
// Return the newly deployed build
|
|
239
|
-
export async function createInitialBuildForNewProject(projectConfig, projectDir, targetAccountId, sendIR, profile) {
|
|
240
|
-
const { result: initialUploadResult, uploadError } = await handleProjectUpload({
|
|
241
|
-
accountId: targetAccountId,
|
|
242
|
-
projectConfig,
|
|
243
|
-
projectDir,
|
|
244
|
-
callbackFunc: projectUploadCallback,
|
|
245
|
-
uploadMessage: lib.localDevHelpers.createInitialBuildForNewProject
|
|
246
|
-
.initialUploadMessage,
|
|
247
|
-
forceCreate: true,
|
|
248
|
-
skipValidation: true,
|
|
249
|
-
sendIR,
|
|
250
|
-
profile,
|
|
251
|
-
});
|
|
252
|
-
if (uploadError) {
|
|
253
|
-
if (isSpecifiedError(uploadError, {
|
|
254
|
-
subCategory: PROJECT_ERROR_TYPES.PROJECT_LOCKED,
|
|
255
|
-
})) {
|
|
256
|
-
uiLogger.log('');
|
|
257
|
-
uiLogger.error(lib.localDevHelpers.createInitialBuildForNewProject.projectLockedError);
|
|
258
|
-
uiLogger.log('');
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
logError(uploadError, new ApiErrorContext({
|
|
262
|
-
accountId: targetAccountId,
|
|
263
|
-
projectName: projectConfig.name,
|
|
264
|
-
}));
|
|
265
|
-
}
|
|
266
|
-
process.exit(EXIT_CODES.ERROR);
|
|
267
|
-
}
|
|
268
|
-
if (!initialUploadResult?.succeeded) {
|
|
269
|
-
let subTasks = [];
|
|
270
|
-
if (initialUploadResult?.buildResult.status === 'FAILURE') {
|
|
271
|
-
subTasks =
|
|
272
|
-
initialUploadResult.buildResult[PROJECT_BUILD_TEXT.SUBTASK_KEY];
|
|
273
|
-
}
|
|
274
|
-
else if (initialUploadResult?.deployResult?.status === 'FAILURE') {
|
|
275
|
-
subTasks =
|
|
276
|
-
initialUploadResult.deployResult[PROJECT_DEPLOY_TEXT.SUBTASK_KEY];
|
|
277
|
-
}
|
|
278
|
-
const failedSubTasks = subTasks.filter(task => task.status === 'FAILURE');
|
|
279
|
-
uiLogger.log('');
|
|
280
|
-
failedSubTasks.forEach(failedSubTask => {
|
|
281
|
-
uiLogger.error(failedSubTask.errorMessage);
|
|
282
|
-
});
|
|
283
|
-
uiLogger.log('');
|
|
284
|
-
process.exit(EXIT_CODES.ERROR);
|
|
285
|
-
}
|
|
286
|
-
return initialUploadResult.buildResult;
|
|
287
|
-
}
|
|
288
|
-
export function getAccountHomeUrl(accountId) {
|
|
289
|
-
const baseUrl = getHubSpotWebsiteOrigin(getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
|
|
290
|
-
return `${baseUrl}/home?portalId=${accountId}`;
|
|
291
|
-
}
|
|
292
192
|
export async function hasSandboxes(account) {
|
|
293
193
|
const accountId = getAccountIdentifier(account);
|
|
294
194
|
if (!accountId) {
|
|
@@ -306,25 +206,25 @@ export async function hasSandboxes(account) {
|
|
|
306
206
|
// Top level prompt to choose the type of account to test on
|
|
307
207
|
export async function selectAccountTypePrompt(accountConfig) {
|
|
308
208
|
const hasAccessToSandboxes = await hasSandboxes(accountConfig);
|
|
309
|
-
const
|
|
209
|
+
const accountId = getAccountIdentifier(accountConfig);
|
|
210
|
+
const result = await listPrompt(lib.localDevHelpers.account.selectAccountTypePrompt.message, {
|
|
310
211
|
choices: [
|
|
311
212
|
{
|
|
312
|
-
name: lib.localDevHelpers.selectAccountTypePrompt
|
|
213
|
+
name: lib.localDevHelpers.account.selectAccountTypePrompt
|
|
313
214
|
.developerTestAccountOption,
|
|
314
215
|
value: HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST,
|
|
315
216
|
},
|
|
316
217
|
{
|
|
317
|
-
name: lib.localDevHelpers.selectAccountTypePrompt
|
|
218
|
+
name: lib.localDevHelpers.account.selectAccountTypePrompt
|
|
318
219
|
.sandboxAccountOption,
|
|
319
220
|
value: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
320
221
|
disabled: !hasAccessToSandboxes
|
|
321
|
-
? lib.localDevHelpers.selectAccountTypePrompt
|
|
222
|
+
? lib.localDevHelpers.account.selectAccountTypePrompt
|
|
322
223
|
.sandboxAccountOptionDisabled
|
|
323
224
|
: false,
|
|
324
225
|
},
|
|
325
226
|
{
|
|
326
|
-
name: lib.localDevHelpers.selectAccountTypePrompt
|
|
327
|
-
.productionAccountOption,
|
|
227
|
+
name: lib.localDevHelpers.account.selectAccountTypePrompt.productionAccountOption(accountId),
|
|
328
228
|
value: null,
|
|
329
229
|
},
|
|
330
230
|
],
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Build } from '@hubspot/local-dev-lib/types/Build';
|
|
2
|
+
import { Project } from '@hubspot/local-dev-lib/types/Project';
|
|
3
|
+
import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
|
|
4
|
+
import { ProjectConfig } from '../../../../types/Projects.js';
|
|
5
|
+
export declare function createNewProjectForLocalDev(projectConfig: ProjectConfig, targetAccountId: number, shouldCreateWithoutConfirmation: boolean, hasPublicApps: boolean): Promise<Project>;
|
|
6
|
+
export declare function createInitialBuildForNewProject(projectConfig: ProjectConfig, projectDir: string, targetAccountId: number, sendIR?: boolean, profile?: string): Promise<Build>;
|
|
7
|
+
export declare function compareLocalProjectToDeployed(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number | undefined, localProjectNodes: {
|
|
8
|
+
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
export declare function isDeployedProjectUpToDateWithLocal(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number, localProjectNodes: {
|
|
11
|
+
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
12
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { createProject } from '@hubspot/local-dev-lib/api/projects';
|
|
5
|
+
import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
|
|
6
|
+
import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
|
|
7
|
+
import { sanitizeFileName } from '@hubspot/local-dev-lib/path';
|
|
8
|
+
import { isDeepEqual } from '@hubspot/local-dev-lib/isDeepEqual';
|
|
9
|
+
import { translate } from '@hubspot/project-parsing-lib';
|
|
10
|
+
import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
|
|
11
|
+
import { PROJECT_ERROR_TYPES, PROJECT_BUILD_TEXT, PROJECT_DEPLOY_TEXT, } from '../../../constants.js';
|
|
12
|
+
import { lib } from '../../../../lang/en.js';
|
|
13
|
+
import { uiLogger } from '../../../ui/logger.js';
|
|
14
|
+
import { uiLine } from '../../../ui/index.js';
|
|
15
|
+
import { confirmPrompt } from '../../../prompts/promptUtils.js';
|
|
16
|
+
import { uiAccountDescription } from '../../../ui/index.js';
|
|
17
|
+
import SpinniesManager from '../../../ui/SpinniesManager.js';
|
|
18
|
+
import { EXIT_CODES } from '../../../enums/exitCodes.js';
|
|
19
|
+
import { handleProjectUpload } from '../../upload.js';
|
|
20
|
+
import { pollProjectBuildAndDeploy } from '../../buildAndDeploy.js';
|
|
21
|
+
import { logError } from '../../../errorHandlers/index.js';
|
|
22
|
+
import { ApiErrorContext } from '../../../errorHandlers/index.js';
|
|
23
|
+
// Prompt the user to create a new project if one doesn't exist on their target account
|
|
24
|
+
export async function createNewProjectForLocalDev(projectConfig, targetAccountId, shouldCreateWithoutConfirmation, hasPublicApps) {
|
|
25
|
+
// Create the project without prompting if this is a newly created sandbox
|
|
26
|
+
let shouldCreateProject = shouldCreateWithoutConfirmation;
|
|
27
|
+
if (!shouldCreateProject) {
|
|
28
|
+
const explanationLangFunction = hasPublicApps
|
|
29
|
+
? lib.localDevHelpers.project.createNewProjectForLocalDev
|
|
30
|
+
.publicAppProjectMustExistExplanation
|
|
31
|
+
: lib.localDevHelpers.project.createNewProjectForLocalDev
|
|
32
|
+
.projectMustExistExplanation;
|
|
33
|
+
const explanationString = explanationLangFunction(projectConfig.name, targetAccountId);
|
|
34
|
+
uiLogger.log('');
|
|
35
|
+
uiLine();
|
|
36
|
+
uiLogger.log(explanationString);
|
|
37
|
+
uiLine();
|
|
38
|
+
shouldCreateProject = await confirmPrompt(lib.localDevHelpers.project.createNewProjectForLocalDev.createProject(projectConfig.name, uiAccountDescription(targetAccountId)));
|
|
39
|
+
}
|
|
40
|
+
if (shouldCreateProject) {
|
|
41
|
+
SpinniesManager.add('createProject', {
|
|
42
|
+
text: lib.localDevHelpers.project.createNewProjectForLocalDev.creatingProject(projectConfig.name, uiAccountDescription(targetAccountId)),
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
const { data: project } = await createProject(targetAccountId, projectConfig.name);
|
|
46
|
+
SpinniesManager.succeed('createProject', {
|
|
47
|
+
text: lib.localDevHelpers.project.createNewProjectForLocalDev.createdProject(projectConfig.name, uiAccountDescription(targetAccountId)),
|
|
48
|
+
succeedColor: 'white',
|
|
49
|
+
});
|
|
50
|
+
return project;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
SpinniesManager.fail('createProject');
|
|
54
|
+
uiLogger.log(lib.localDevHelpers.project.createNewProjectForLocalDev
|
|
55
|
+
.failedToCreateProject);
|
|
56
|
+
process.exit(EXIT_CODES.ERROR);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// We cannot continue if the project does not exist in the target account
|
|
61
|
+
uiLogger.log('');
|
|
62
|
+
uiLogger.log(lib.localDevHelpers.project.createNewProjectForLocalDev
|
|
63
|
+
.choseNotToCreateProject);
|
|
64
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function projectUploadCallback(accountId, projectConfig, tempFile, buildId) {
|
|
68
|
+
if (!buildId) {
|
|
69
|
+
uiLogger.error(lib.localDevHelpers.project.createInitialBuildForNewProject.genericError);
|
|
70
|
+
process.exit(EXIT_CODES.ERROR);
|
|
71
|
+
}
|
|
72
|
+
return pollProjectBuildAndDeploy(accountId, projectConfig, tempFile, buildId, true);
|
|
73
|
+
}
|
|
74
|
+
// Create an initial build if the project was newly created in the account
|
|
75
|
+
// Return the newly deployed build
|
|
76
|
+
export async function createInitialBuildForNewProject(projectConfig, projectDir, targetAccountId, sendIR, profile) {
|
|
77
|
+
const { result: initialUploadResult, uploadError } = await handleProjectUpload({
|
|
78
|
+
accountId: targetAccountId,
|
|
79
|
+
projectConfig,
|
|
80
|
+
projectDir,
|
|
81
|
+
callbackFunc: projectUploadCallback,
|
|
82
|
+
uploadMessage: lib.localDevHelpers.project.createInitialBuildForNewProject
|
|
83
|
+
.initialUploadMessage,
|
|
84
|
+
forceCreate: true,
|
|
85
|
+
skipValidation: true,
|
|
86
|
+
sendIR,
|
|
87
|
+
profile,
|
|
88
|
+
});
|
|
89
|
+
if (uploadError) {
|
|
90
|
+
if (isSpecifiedError(uploadError, {
|
|
91
|
+
subCategory: PROJECT_ERROR_TYPES.PROJECT_LOCKED,
|
|
92
|
+
})) {
|
|
93
|
+
uiLogger.log('');
|
|
94
|
+
uiLogger.error(lib.localDevHelpers.project.createInitialBuildForNewProject
|
|
95
|
+
.projectLockedError);
|
|
96
|
+
uiLogger.log('');
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
logError(uploadError, new ApiErrorContext({
|
|
100
|
+
accountId: targetAccountId,
|
|
101
|
+
projectName: projectConfig.name,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
process.exit(EXIT_CODES.ERROR);
|
|
105
|
+
}
|
|
106
|
+
if (!initialUploadResult?.succeeded) {
|
|
107
|
+
let subTasks = [];
|
|
108
|
+
if (initialUploadResult?.buildResult.status === 'FAILURE') {
|
|
109
|
+
subTasks =
|
|
110
|
+
initialUploadResult.buildResult[PROJECT_BUILD_TEXT.SUBTASK_KEY];
|
|
111
|
+
}
|
|
112
|
+
else if (initialUploadResult?.deployResult?.status === 'FAILURE') {
|
|
113
|
+
subTasks =
|
|
114
|
+
initialUploadResult.deployResult[PROJECT_DEPLOY_TEXT.SUBTASK_KEY];
|
|
115
|
+
}
|
|
116
|
+
const failedSubTasks = subTasks.filter(task => task.status === 'FAILURE');
|
|
117
|
+
uiLogger.log('');
|
|
118
|
+
failedSubTasks.forEach(failedSubTask => {
|
|
119
|
+
uiLogger.error(failedSubTask.errorMessage);
|
|
120
|
+
});
|
|
121
|
+
uiLogger.log('');
|
|
122
|
+
process.exit(EXIT_CODES.ERROR);
|
|
123
|
+
}
|
|
124
|
+
return initialUploadResult.buildResult;
|
|
125
|
+
}
|
|
126
|
+
export async function compareLocalProjectToDeployed(projectConfig, accountId, deployedBuildId, localProjectNodes) {
|
|
127
|
+
uiLogger.log('');
|
|
128
|
+
if (!deployedBuildId) {
|
|
129
|
+
uiLogger.error(lib.localDevHelpers.project.compareLocalProjectToDeployed.noDeployedBuild(projectConfig.name, uiAccountDescription(accountId)));
|
|
130
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
131
|
+
}
|
|
132
|
+
SpinniesManager.add('compareLocalProjectToDeployed', {
|
|
133
|
+
text: lib.localDevHelpers.project.compareLocalProjectToDeployed.checking,
|
|
134
|
+
});
|
|
135
|
+
const isUpToDate = await isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes);
|
|
136
|
+
if (isUpToDate) {
|
|
137
|
+
SpinniesManager.succeed('compareLocalProjectToDeployed', {
|
|
138
|
+
text: lib.localDevHelpers.project.compareLocalProjectToDeployed.upToDate,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
SpinniesManager.fail('compareLocalProjectToDeployed', {
|
|
143
|
+
text: lib.localDevHelpers.project.compareLocalProjectToDeployed
|
|
144
|
+
.notUpToDate,
|
|
145
|
+
});
|
|
146
|
+
uiLogger.log('');
|
|
147
|
+
uiLogger.log(lib.localDevHelpers.project.compareLocalProjectToDeployed
|
|
148
|
+
.notUpToDateExplanation);
|
|
149
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export async function isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes) {
|
|
153
|
+
let tempDir = null;
|
|
154
|
+
try {
|
|
155
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'hubspot-project-compare-'));
|
|
156
|
+
const { data: zippedProject } = await downloadProject(accountId, projectConfig.name, deployedBuildId);
|
|
157
|
+
const extractedProjectPath = path.join(tempDir, sanitizeFileName(projectConfig.name));
|
|
158
|
+
await extractZipArchive(zippedProject, sanitizeFileName(projectConfig.name), tempDir, { includesRootDir: false, hideLogs: true });
|
|
159
|
+
const deployedProjectSourceDir = path.join(extractedProjectPath, projectConfig.srcDir);
|
|
160
|
+
const { intermediateNodesIndexedByUid: deployedProjectNodes } = await translate({
|
|
161
|
+
projectSourceDir: deployedProjectSourceDir,
|
|
162
|
+
platformVersion: projectConfig.platformVersion,
|
|
163
|
+
accountId: accountId,
|
|
164
|
+
}, {});
|
|
165
|
+
return isDeepEqual(localProjectNodes, deployedProjectNodes, ['localDev']);
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
// Clean up temporary directory
|
|
169
|
+
if (tempDir && (await fs.pathExists(tempDir))) {
|
|
170
|
+
await fs.remove(tempDir);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
package/lib/projects/urls.d.ts
CHANGED
|
@@ -6,3 +6,4 @@ export declare function getProjectActivityUrl(projectName: string, accountId: nu
|
|
|
6
6
|
export declare function getProjectBuildDetailUrl(projectName: string, buildId: number, accountId: number): string;
|
|
7
7
|
export declare function getProjectDeployDetailUrl(projectName: string, deployId: number, accountId: number): string;
|
|
8
8
|
export declare function getLocalDevUiUrl(accountId: number, showWelcomeScreen?: boolean): string;
|
|
9
|
+
export declare function getAccountHomeUrl(accountId: number): string;
|
package/lib/projects/urls.js
CHANGED
|
@@ -37,3 +37,7 @@ export function getProjectDeployDetailUrl(projectName, deployId, accountId) {
|
|
|
37
37
|
export function getLocalDevUiUrl(accountId, showWelcomeScreen) {
|
|
38
38
|
return `${getBaseUrl(accountId)}/developer-projects-local-dev/${accountId}${showWelcomeScreen ? '?welcome' : ''}`;
|
|
39
39
|
}
|
|
40
|
+
export function getAccountHomeUrl(accountId) {
|
|
41
|
+
const baseUrl = getHubSpotWebsiteOrigin(getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
|
|
42
|
+
return `${baseUrl}/home?portalId=${accountId}`;
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { createFunctionPrompt } from '../createFunctionPrompt.js';
|
|
3
|
+
import { promptUser } from '../promptUtils.js';
|
|
4
|
+
vi.mock('../promptUtils.js');
|
|
5
|
+
const mockPromptUser = vi.mocked(promptUser);
|
|
6
|
+
describe('createFunctionPrompt', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.resetAllMocks();
|
|
9
|
+
});
|
|
10
|
+
describe('when all parameters are provided', () => {
|
|
11
|
+
it('should return provided values without prompting', async () => {
|
|
12
|
+
const commandArgs = {
|
|
13
|
+
functionsFolder: 'my-functions',
|
|
14
|
+
filename: 'my-function',
|
|
15
|
+
endpointMethod: 'POST',
|
|
16
|
+
endpointPath: '/api/test',
|
|
17
|
+
};
|
|
18
|
+
const result = await createFunctionPrompt(commandArgs);
|
|
19
|
+
expect(mockPromptUser).not.toHaveBeenCalled();
|
|
20
|
+
expect(result).toEqual({
|
|
21
|
+
functionsFolder: 'my-functions',
|
|
22
|
+
filename: 'my-function',
|
|
23
|
+
endpointMethod: 'POST',
|
|
24
|
+
endpointPath: '/api/test',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it('should use default GET method when endpointMethod not provided', async () => {
|
|
28
|
+
const commandArgs = {
|
|
29
|
+
functionsFolder: 'my-functions',
|
|
30
|
+
filename: 'my-function',
|
|
31
|
+
endpointPath: '/api/test',
|
|
32
|
+
};
|
|
33
|
+
const result = await createFunctionPrompt(commandArgs);
|
|
34
|
+
expect(mockPromptUser).not.toHaveBeenCalled();
|
|
35
|
+
expect(result).toEqual({
|
|
36
|
+
functionsFolder: 'my-functions',
|
|
37
|
+
filename: 'my-function',
|
|
38
|
+
endpointMethod: 'GET',
|
|
39
|
+
endpointPath: '/api/test',
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('when some parameters are missing', () => {
|
|
44
|
+
it('should only prompt for missing parameters', async () => {
|
|
45
|
+
const commandArgs = {
|
|
46
|
+
functionsFolder: 'my-functions',
|
|
47
|
+
endpointMethod: 'POST',
|
|
48
|
+
};
|
|
49
|
+
mockPromptUser.mockResolvedValue({
|
|
50
|
+
filename: 'prompted-function',
|
|
51
|
+
endpointPath: '/prompted-path',
|
|
52
|
+
});
|
|
53
|
+
const result = await createFunctionPrompt(commandArgs);
|
|
54
|
+
expect(mockPromptUser).toHaveBeenCalledWith([
|
|
55
|
+
expect.objectContaining({ name: 'filename' }),
|
|
56
|
+
expect.objectContaining({ name: 'endpointPath' }),
|
|
57
|
+
]);
|
|
58
|
+
expect(result).toEqual({
|
|
59
|
+
functionsFolder: 'my-functions',
|
|
60
|
+
filename: 'prompted-function',
|
|
61
|
+
endpointMethod: 'POST',
|
|
62
|
+
endpointPath: '/prompted-path',
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('when no parameters are provided', () => {
|
|
67
|
+
it('should prompt for all parameters', async () => {
|
|
68
|
+
mockPromptUser.mockResolvedValue({
|
|
69
|
+
functionsFolder: 'prompted-functions',
|
|
70
|
+
filename: 'prompted-function',
|
|
71
|
+
endpointMethod: 'GET',
|
|
72
|
+
endpointPath: '/prompted-path',
|
|
73
|
+
});
|
|
74
|
+
const result = await createFunctionPrompt();
|
|
75
|
+
expect(mockPromptUser).toHaveBeenCalledWith([
|
|
76
|
+
expect.objectContaining({ name: 'functionsFolder' }),
|
|
77
|
+
expect.objectContaining({ name: 'filename' }),
|
|
78
|
+
expect.objectContaining({ name: 'endpointMethod' }),
|
|
79
|
+
expect.objectContaining({ name: 'endpointPath' }),
|
|
80
|
+
]);
|
|
81
|
+
expect(result).toEqual({
|
|
82
|
+
functionsFolder: 'prompted-functions',
|
|
83
|
+
filename: 'prompted-function',
|
|
84
|
+
endpointMethod: 'GET',
|
|
85
|
+
endpointPath: '/prompted-path',
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('parameter precedence', () => {
|
|
90
|
+
it('should prioritize command args over prompted values', async () => {
|
|
91
|
+
const commandArgs = {
|
|
92
|
+
functionsFolder: 'arg-functions',
|
|
93
|
+
};
|
|
94
|
+
mockPromptUser.mockResolvedValue({
|
|
95
|
+
filename: 'prompted-function',
|
|
96
|
+
endpointMethod: 'POST',
|
|
97
|
+
endpointPath: '/prompted-path',
|
|
98
|
+
});
|
|
99
|
+
const result = await createFunctionPrompt(commandArgs);
|
|
100
|
+
expect(result).toEqual({
|
|
101
|
+
functionsFolder: 'arg-functions', // from commandArgs
|
|
102
|
+
filename: 'prompted-function', // from prompt
|
|
103
|
+
endpointMethod: 'POST', // from prompt
|
|
104
|
+
endpointPath: '/prompted-path', // from prompt
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
it('should handle mixed scenario with partial command args and prompting', async () => {
|
|
108
|
+
const commandArgs = {
|
|
109
|
+
functionsFolder: 'my-funcs',
|
|
110
|
+
endpointMethod: 'DELETE',
|
|
111
|
+
};
|
|
112
|
+
mockPromptUser.mockResolvedValue({
|
|
113
|
+
filename: 'delete-handler',
|
|
114
|
+
endpointPath: '/api/delete',
|
|
115
|
+
});
|
|
116
|
+
const result = await createFunctionPrompt(commandArgs);
|
|
117
|
+
expect(mockPromptUser).toHaveBeenCalledWith([
|
|
118
|
+
expect.objectContaining({ name: 'filename' }),
|
|
119
|
+
expect.objectContaining({ name: 'endpointPath' }),
|
|
120
|
+
]);
|
|
121
|
+
expect(result).toEqual({
|
|
122
|
+
functionsFolder: 'my-funcs', // from commandArgs
|
|
123
|
+
filename: 'delete-handler', // from prompt
|
|
124
|
+
endpointMethod: 'DELETE', // from commandArgs
|
|
125
|
+
endpointPath: '/api/delete', // from prompt
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|