Skip to content

Commit

Permalink
Merge pull request #1112 from HubSpot/sandbox-create-refactor
Browse files Browse the repository at this point in the history
Sandbox command updates
  • Loading branch information
adamawang authored Jul 23, 2024
2 parents 093562c + 249c664 commit 95f60b6
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 374 deletions.
57 changes: 11 additions & 46 deletions packages/cli/commands/sandbox/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ const { loadAndValidateOptions } = require('../../lib/validation');
const { i18n } = require('../../lib/lang');
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
const { getAccountConfig, getEnv } = require('@hubspot/local-dev-lib/config');
const {
uiFeatureHighlight,
uiAccountDescription,
uiBetaTag,
} = require('../../lib/ui');
const { uiFeatureHighlight, uiBetaTag } = require('../../lib/ui');
const {
sandboxTypeMap,
getAvailableSyncTypes,
Expand All @@ -22,10 +18,7 @@ const {
} = require('../../lib/sandboxes');
const { getValidEnv } = require('@hubspot/local-dev-lib/environment');
const { logger } = require('@hubspot/local-dev-lib/logger');
const {
trackCommandUsage,
trackCommandMetadataUsage,
} = require('../../lib/usageTracking');
const { trackCommandUsage } = require('../../lib/usageTracking');
const { sandboxTypePrompt } = require('../../lib/prompts/sandboxesPrompt');
const { promptUser } = require('../../lib/prompts/promptUtils');
const { syncSandbox } = require('../../lib/sandboxSync');
Expand Down Expand Up @@ -124,34 +117,18 @@ exports.handler = async options => {
}
const sandboxName = name || namePrompt.name;

let sandboxSyncPromptResult = true;
let contactRecordsSyncPromptResult = true;
let contactRecordsSyncPromptResult = false;
if (!force) {
const syncI18nKey = 'lib.sandbox.sync';
const sandboxLangKey =
sandboxType === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX
? 'developer'
: 'standard';
const { sandboxSyncPrompt } = await promptUser([
{
name: 'sandboxSyncPrompt',
type: 'confirm',
message: i18n(`${syncI18nKey}.confirm.createFlow.${sandboxLangKey}`, {
parentAccountName: uiAccountDescription(accountId),
sandboxName,
}),
},
]);
sandboxSyncPromptResult = sandboxSyncPrompt;
// We can prompt for contact records before fetching types since we're starting with a fresh sandbox in create
if (sandboxSyncPrompt) {
const isStandardSandbox =
sandboxType === HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX;

// Prompt to sync contact records for standard sandboxes only
if (isStandardSandbox) {
const { contactRecordsSyncPrompt } = await promptUser([
{
name: 'contactRecordsSyncPrompt',
type: 'confirm',
message: i18n(
`${syncI18nKey}.confirm.syncContactRecords.${sandboxLangKey}`
),
message: i18n('lib.sandbox.sync.confirm.syncContactRecords.standard'),
},
]);
contactRecordsSyncPromptResult = contactRecordsSyncPrompt;
Expand All @@ -167,15 +144,8 @@ exports.handler = async options => {
force,
});

// Prompt user to sync assets after sandbox creation
const sandboxAccountConfig = getAccountConfig(result.sandbox.sandboxHubId);
const handleSyncSandbox = async syncTasks => {
// Send tracking event for secondary action, in this case a sandbox sync within the sandbox create flow
trackCommandMetadataUsage(
'sandbox-sync',
{ step: 'sandbox-create' },
result.sandbox.sandboxHubId
);
await syncSandbox({
accountConfig: sandboxAccountConfig,
parentAccountConfig: accountConfig,
Expand All @@ -188,18 +158,13 @@ exports.handler = async options => {
accountConfig,
sandboxAccountConfig
);

if (!contactRecordsSyncPromptResult) {
availableSyncTasks = availableSyncTasks.filter(
t => t.type !== syncTypes.OBJECT_RECORDS
);
}
if (!force) {
if (sandboxSyncPromptResult) {
await handleSyncSandbox(availableSyncTasks);
}
} else {
await handleSyncSandbox(availableSyncTasks);
}
await handleSyncSandbox(availableSyncTasks);
} catch (err) {
logErrorInstance(err);
throw err;
Expand Down
14 changes: 12 additions & 2 deletions packages/cli/commands/sandbox/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
const { getAccountConfig, getEnv } = require('@hubspot/local-dev-lib/config');
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
const { promptUser } = require('../../lib/prompts/promptUtils');
const { uiLine, uiAccountDescription, uiBetaTag } = require('../../lib/ui');
const {
uiLine,
uiAccountDescription,
uiDeprecatedMessage,
uiDeprecatedDescription,
} = require('../../lib/ui');
const {
isSandbox,
isStandardSandbox,
Expand All @@ -35,11 +40,16 @@ const {
const i18nKey = 'commands.sandbox.subcommands.sync';

exports.command = 'sync';
exports.describe = uiBetaTag(i18n(`${i18nKey}.describe`), false);
exports.describe = uiDeprecatedDescription(
i18n(`${i18nKey}.describe`),
'hs sandbox sync'
);

exports.handler = async options => {
await loadAndValidateOptions(options);

uiDeprecatedMessage('hs sandbox sync');

const { force } = options; // For scripting purposes
const accountId = getAccountId(options);
const accountConfig = getAccountConfig(accountId);
Expand Down
57 changes: 20 additions & 37 deletions packages/cli/lang/en.lyaml
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,14 @@ en:
betaWarning:
header: "{{#yellow}}***************************** WARNING ****************************{{/yellow}}"
footer: "{{#yellow}}******************************************************************{{/yellow}}"
infoTag: "{{#bold}}[INFO]{{/bold}}"
deprecatedTag: "{{#bold}}[DEPRECATED]{{/bold}}"
errorTag: "{{#bold}}[ERROR]{{/bold}}"
deprecatedMessage: "The {{ command }} command is deprecated and will be disabled soon. {{ url }}"
deprecatedDescription: "{{ message }}. The {{ command }} command is deprecated and will be disabled soon. {{ url }}"
deprecatedUrlText: 'Learn more.'
disabledMessage: "The {{ command }} command is disabled. Run {{ npmCommand }} to update to the latest HubSpot CLI version. {{ url }}"
disabledUrlText: "See all HubSpot CLI commands here."
featureHighlight:
defaultTitle: "What's next?"
commandKeys:
Expand Down Expand Up @@ -1263,9 +1271,9 @@ en:
selectAccountName: "Select the sandbox account you want to delete"
selectParentAccountName: "Select the account that the sandbox belongs to"
type:
message: "What type of sandbox would you like to create?"
developer: "Development sandbox (Isolated environment for developers)"
standard: "Standard sandbox (Testing environment for all Super Admins)"
message: "Choose the type of sandbox you want to create"
developer: "Development sandbox (Includes production's object definitions)"
standard: "Standard sandbox (Includes partial copy of production's assets)"
uploadPrompt:
enterDest: "[--dest] Enter the destination path: "
enterSrc: "[--src] Enter the source path: "
Expand Down Expand Up @@ -1336,11 +1344,11 @@ en:
developer:
add: "Creating development sandbox {{#bold}}{{ accountName }}{{/bold}}"
fail: "Failed to create a development sandbox {{#bold}}{{ accountName }}{{/bold}}."
succeed: "Successfully created a development sandbox {{#bold}}{{ accountName }}{{/bold}} with portalId {{#bold}}{{ accountId }}{{/bold}}."
succeed: "Created {{#bold}}{{ accountName }} [dev sandbox] ({{ accountId }}){{/bold}}."
standard:
add: "Creating standard sandbox {{#bold}}{{ accountName }}{{/bold}}"
fail: "Failed to create a standard sandbox {{#bold}}{{ accountName }}{{/bold}}."
succeed: "Successfully created a standard sandbox {{#bold}}{{ accountName }}{{/bold}} with portalId {{#bold}}{{ accountId }}{{/bold}}."
succeed: "Created {{#bold}}{{ accountName }} [standard sandbox] ({{ accountId }}){{/bold}}."
failure:
invalidUser: "Couldn't create {{#bold}}{{ accountName }}{{/bold}} because your account has been removed from {{#bold}}{{ parentAccountName }}{{/bold}} or your permission set doesn't allow you to create the sandbox. To update your permissions, contact a super admin in {{#bold}}{{ parentAccountName }}{{/bold}}."
403Gating: "Couldn't create {{#bold}}{{ accountName }}{{/bold}} because {{#bold}}{{ parentAccountName }}{{/bold}} does not have access to development sandboxes. To opt in to the CRM Development Beta and use development sandboxes, visit https://app.hubspot.com/l/product-updates/in-beta?update=13899236."
Expand Down Expand Up @@ -1382,53 +1390,28 @@ en:
sync:
info:
syncStatus: "View the sync status details at: {{#bold}}{{ url }}{{/bold}}"
earlyExit: "Syncing may take some time. Hit {{#bold}}Enter{{/bold}} or {{#bold}}Ctrl+C{{/bold}} to exit and continue the sync in the background.\n"
syncMessage: "Asset sync from production to the sandbox is in progress and is running in the background. It may take some time. {{ url }}"
syncMessageDevSb: "Sync of object definitions from production to the sandbox is in progress and is running in the background. It may take some time. {{ url }}"
syncStatusDetailsLinkText: "View sync status details here"
confirm:
createFlow:
standard: "Sync all supported assets to {{#cyan}}{{#bold}}{{ sandboxName }}{{/bold}}{{/cyan}} from {{#bold}}{{ parentAccountName }}{{/bold}}?"
developer: "Sync CRM object definitions to {{#cyan}}{{#bold}}{{ sandboxName }}{{/bold}}{{/cyan}} from {{#bold}}{{ parentAccountName }}{{/bold}}?"
syncContactRecords:
standard: "Include up to 5000 most recently updated contacts? This includes up to 100 of each of the following: associated deals, tickets, and companies. This can be done once per sandbox."
standard: "Copy up to 5000 most recently updated contacts? This includes up to 100 of each of the following: associated deals, tickets, and companies."
developer: "Include up to 100 most recently updated contacts? This includes up to 100 of each of the following: associated deals, tickets, and companies. This can be done once per sandbox."
loading:
startSync: "Initiating sync..."
fail: "Failed to sync sandbox."
succeed: "Sandbox sync initiated to {{ accountName }}."
skipPolling: "Syncing CRM object definitions."
skipPollingWithContacts: "Syncing CRM object definitions and up to 100 most recently updated contacts with associated deals, tickets, and companies (up to 100 each)."
polling:
syncing: "Syncing sandbox..."
fail: "Failed to fetch sync updates. View the sync status at: {{ url }}"
succeed: "Sandbox sync complete."
succeed: "Initiated asset sync from production to {{ accountName }}"
succeedDevSb: "Initiated sync of object definitions from production to {{ accountName }}"
successDevSbInfo: "Initiated sync of object definitions from production to {{ accountName }}. It may take some time. {{ url }}"
failure:
invalidUser: "Couldn't sync {{ accountName }} because your account has been removed from {{ parentAccountName }} or your permission set doesn't allow you to sync the sandbox. To update your permissions, contact a super admin in {{ parentAccountName }}."
missingScopes: "Couldn’t run the sync because there are scopes missing in your production account. To update scopes, deactivate your current personal access key for {{#bold}}{{ accountName }}{{/bold}}, and generate a new one. Then run `hs auth` to update the CLI with the new key."
syncInProgress: "Couldn’t run the sync because there’s another sync in progress. Wait for the current sync to finish and then try again. To check the sync status, visit the sync activity log: {{ url }}."
notSuperAdmin: "Couldn't run the sync because you are not a super admin in {{ account }}. Ask the account owner for super admin access to the sandbox."
objectNotFound: "Couldn't sync the sandbox because {{#bold}}{{ account }}{{/bold}} may have been deleted through the UI. Run {{#bold}}hs sandbox delete{{/bold}} to remove this account from the config. "
types:
parcels:
label: "Account tools and features"
super-admins:
label: "Super Admins"
object-schemas:
label: "Object definitions"
object-records:
label: "Contacts and associated records"
cms-developer-assets:
label: "Themes, templates, and modules"
object-pipelines:
label: "Pipelines"
object-lists:
label: "Lists"
workflows:
label: "Workflows"
forms:
label: "Forms"
lead-flows:
label: "Lead Flows"
marketing-email:
label: "Marketing emails"
errorHandlers:
standardErrors:
errorOccurred: "Error: {{ error }}"
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/lib/interpolationHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ module.exports = {
cyan: function(stringValue) {
return chalk.cyan(stringValue);
},
orange: function(stringValue) {
return chalk.hex('#FC9900')(stringValue);
},
};
3 changes: 1 addition & 2 deletions packages/cli/lib/localDev.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,7 @@ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
parentAccountConfig: accountConfig,
env,
syncTasks,
allowEarlyTermination: false, // Don't let user terminate early in this flow
skipPolling: true, // Skip polling, sync will run and complete in the background
slimInfoMessage: true,
});
return targetAccountId;
} catch (err) {
Expand Down
5 changes: 1 addition & 4 deletions packages/cli/lib/prompts/accountNamePrompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ const hubspotAccountNamePrompt = ({ accountType, currentPortalCount = 0 }) => {
promptMessageString = isDevelopmentSandbox
? i18n(`${i18nKey}.enterDevelopmentSandboxName`)
: i18n(`${i18nKey}.enterStandardSandboxName`);
defaultName = i18n(`${i18nKey}.sandboxDefaultName`, {
sandboxType: isDevelopmentSandbox ? 'development' : 'standard',
});
} else if (isDeveloperTestAccount) {
promptMessageString = i18n(`${i18nKey}.enterDeveloperTestAccountName`);
defaultName = i18n(`${i18nKey}.developerTestAccountDefaultName`, {
Expand All @@ -62,7 +59,7 @@ const hubspotAccountNamePrompt = ({ accountType, currentPortalCount = 0 }) => {
validate(val) {
if (typeof val !== 'string') {
return i18n(`${i18nKey}.errors.invalidName`);
} else if (!val.length) {
} else if (!val.trim().length) {
return i18n(`${i18nKey}.errors.nameRequired`);
}
return accountNameExistsInConfig(val)
Expand Down
Loading

0 comments on commit 95f60b6

Please sign in to comment.