@hubspot/cli 4.1.7-beta.1 → 4.1.7
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/module/marketplace-validate.js +27 -37
- package/commands/sandbox/delete.js +57 -25
- package/commands/theme/marketplace-validate.js +15 -125
- package/lib/marketplace-validate.js +137 -0
- package/lib/prompts/accountsPrompt.js +1 -0
- package/lib/prompts/sandboxesPrompt.js +19 -1
- package/package.json +4 -4
- package/lib/validators/__tests__/ModuleDependencyValidator.js +0 -79
- package/lib/validators/__tests__/ModuleValidator.js +0 -51
- package/lib/validators/__tests__/RelativeValidator.js +0 -28
- package/lib/validators/__tests__/validatorTestUtils.js +0 -8
- package/lib/validators/applyValidators.js +0 -17
- package/lib/validators/constants.js +0 -11
- package/lib/validators/index.js +0 -8
- package/lib/validators/logValidatorResults.js +0 -59
- package/lib/validators/marketplaceValidators/RelativeValidator.js +0 -46
- package/lib/validators/marketplaceValidators/module/ModuleDependencyValidator.js +0 -101
- package/lib/validators/marketplaceValidators/module/ModuleValidator.js +0 -102
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const Spinnies = require('spinnies');
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
addConfigOptions,
|
|
@@ -9,17 +9,15 @@ const {
|
|
|
9
9
|
const { loadAndValidateOptions } = require('../../lib/validation');
|
|
10
10
|
const { trackCommandUsage } = require('../../lib/usageTracking');
|
|
11
11
|
const {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const { VALIDATION_RESULT } = require('../../lib/validators/constants');
|
|
12
|
+
kickOffValidation,
|
|
13
|
+
pollForValidationFinish,
|
|
14
|
+
fetchValidationResults,
|
|
15
|
+
processValidationErrors,
|
|
16
|
+
displayValidationResults,
|
|
17
|
+
} = require('../../lib/marketplace-validate');
|
|
19
18
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
20
19
|
|
|
21
20
|
const i18nKey = 'cli.commands.module.subcommands.marketplaceValidate';
|
|
22
|
-
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
23
21
|
|
|
24
22
|
exports.command = 'marketplace-validate <src>';
|
|
25
23
|
exports.describe = i18n(`${i18nKey}.describe`);
|
|
@@ -31,31 +29,29 @@ exports.handler = async options => {
|
|
|
31
29
|
|
|
32
30
|
const accountId = getAccountId(options);
|
|
33
31
|
|
|
34
|
-
if (!options.json) {
|
|
35
|
-
logger.log(
|
|
36
|
-
i18n(`${i18nKey}.logs.validatingModule`, {
|
|
37
|
-
path: src,
|
|
38
|
-
})
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
32
|
trackCommandUsage('validate', null, accountId);
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
).then(groupedResults => {
|
|
49
|
-
logValidatorResults(groupedResults, { logAsJson: options.json });
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
groupedResults
|
|
53
|
-
.flat()
|
|
54
|
-
.some(result => result.result === VALIDATION_RESULT.FATAL)
|
|
55
|
-
) {
|
|
56
|
-
process.exit(EXIT_CODES.WARNING);
|
|
57
|
-
}
|
|
34
|
+
const spinnies = new Spinnies();
|
|
35
|
+
spinnies.add('marketplaceValidation', {
|
|
36
|
+
text: i18n(`${i18nKey}.logs.validatingModule`, {
|
|
37
|
+
path: src,
|
|
38
|
+
}),
|
|
58
39
|
});
|
|
40
|
+
|
|
41
|
+
const assetType = 'MODULE';
|
|
42
|
+
const validationId = await kickOffValidation(accountId, assetType, src);
|
|
43
|
+
await pollForValidationFinish(accountId, validationId);
|
|
44
|
+
|
|
45
|
+
spinnies.remove('marketplaceValidation');
|
|
46
|
+
|
|
47
|
+
const validationResults = await fetchValidationResults(
|
|
48
|
+
accountId,
|
|
49
|
+
validationId
|
|
50
|
+
);
|
|
51
|
+
processValidationErrors(validationResults);
|
|
52
|
+
displayValidationResults(i18nKey, validationResults);
|
|
53
|
+
|
|
54
|
+
process.exit();
|
|
59
55
|
};
|
|
60
56
|
|
|
61
57
|
exports.builder = yargs => {
|
|
@@ -63,12 +59,6 @@ exports.builder = yargs => {
|
|
|
63
59
|
addAccountOptions(yargs, true);
|
|
64
60
|
addUseEnvironmentOptions(yargs, true);
|
|
65
61
|
|
|
66
|
-
yargs.options({
|
|
67
|
-
json: {
|
|
68
|
-
describe: i18n(`${i18nKey}.options.json.describe`),
|
|
69
|
-
type: 'boolean',
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
62
|
yargs.positional('src', {
|
|
73
63
|
describe: i18n(`${i18nKey}.positionals.src.describe`),
|
|
74
64
|
type: 'string',
|
|
@@ -18,6 +18,7 @@ const { getConfig, getEnv } = require('@hubspot/cli-lib');
|
|
|
18
18
|
const { deleteSandboxPrompt } = require('../../lib/prompts/sandboxesPrompt');
|
|
19
19
|
const {
|
|
20
20
|
removeSandboxAccountFromConfig,
|
|
21
|
+
updateDefaultAccount,
|
|
21
22
|
} = require('@hubspot/cli-lib/lib/config');
|
|
22
23
|
const {
|
|
23
24
|
selectAndSetAsDefaultAccountPrompt,
|
|
@@ -38,13 +39,26 @@ exports.describe = i18n(`${i18nKey}.describe`);
|
|
|
38
39
|
exports.handler = async options => {
|
|
39
40
|
await loadAndValidateOptions(options, false);
|
|
40
41
|
|
|
41
|
-
const { account } = options;
|
|
42
|
+
const { account, force } = options;
|
|
42
43
|
const config = getConfig();
|
|
43
44
|
|
|
44
45
|
let accountPrompt;
|
|
45
46
|
if (!account) {
|
|
46
|
-
|
|
47
|
+
if (!force) {
|
|
48
|
+
accountPrompt = await deleteSandboxPrompt(config);
|
|
49
|
+
} else {
|
|
50
|
+
// Account is required, throw error if force flag is present and no account is specified
|
|
51
|
+
logger.log('');
|
|
52
|
+
logger.error(i18n(`${i18nKey}.failure.noAccount`));
|
|
53
|
+
process.exit(EXIT_CODES.ERROR);
|
|
54
|
+
}
|
|
55
|
+
if (!accountPrompt) {
|
|
56
|
+
logger.log('');
|
|
57
|
+
logger.error(i18n(`${i18nKey}.failure.noSandboxAccounts`));
|
|
58
|
+
process.exit(EXIT_CODES.ERROR);
|
|
59
|
+
}
|
|
47
60
|
}
|
|
61
|
+
|
|
48
62
|
const sandboxAccountId = getAccountId({
|
|
49
63
|
account: account || accountPrompt.account,
|
|
50
64
|
});
|
|
@@ -54,31 +68,36 @@ exports.handler = async options => {
|
|
|
54
68
|
|
|
55
69
|
trackCommandUsage('sandbox-delete', null, sandboxAccountId);
|
|
56
70
|
|
|
71
|
+
const baseUrl = getHubSpotWebsiteOrigin(
|
|
72
|
+
getEnv(sandboxAccountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
|
|
73
|
+
);
|
|
74
|
+
|
|
57
75
|
let parentAccountId;
|
|
58
76
|
for (const portal of config.portals) {
|
|
59
77
|
if (portal.portalId === sandboxAccountId) {
|
|
60
78
|
if (portal.parentAccountId) {
|
|
61
79
|
parentAccountId = portal.parentAccountId;
|
|
62
|
-
} else {
|
|
80
|
+
} else if (!force) {
|
|
63
81
|
const parentAccountPrompt = await deleteSandboxPrompt(config, true);
|
|
64
82
|
parentAccountId = getAccountId({
|
|
65
83
|
account: parentAccountPrompt.account,
|
|
66
84
|
});
|
|
85
|
+
} else {
|
|
86
|
+
logger.error(i18n(`${i18nKey}.failure.noParentAccount`));
|
|
87
|
+
process.exit(EXIT_CODES.ERROR);
|
|
67
88
|
}
|
|
68
89
|
}
|
|
69
90
|
}
|
|
70
91
|
|
|
92
|
+
const url = `${baseUrl}/sandboxes/${parentAccountId}`;
|
|
93
|
+
const command = `hs auth ${
|
|
94
|
+
getEnv(sandboxAccountId) === 'qa' ? '--qa' : ''
|
|
95
|
+
} --account=${parentAccountId}`;
|
|
96
|
+
|
|
71
97
|
if (!getAccountId({ account: parentAccountId })) {
|
|
72
|
-
const baseUrl = getHubSpotWebsiteOrigin(
|
|
73
|
-
getEnv(sandboxAccountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
|
|
74
|
-
);
|
|
75
|
-
const url = `${baseUrl}/sandboxes/${parentAccountId}`;
|
|
76
|
-
const command = `hs auth ${
|
|
77
|
-
getEnv(sandboxAccountId) === 'qa' ? '--qa' : ''
|
|
78
|
-
} --account=${parentAccountId}`;
|
|
79
98
|
logger.log('');
|
|
80
99
|
logger.error(
|
|
81
|
-
i18n(`${i18nKey}.noParentPortalAvailable`, {
|
|
100
|
+
i18n(`${i18nKey}.failure.noParentPortalAvailable`, {
|
|
82
101
|
parentAccountId,
|
|
83
102
|
url,
|
|
84
103
|
command,
|
|
@@ -103,17 +122,19 @@ exports.handler = async options => {
|
|
|
103
122
|
}
|
|
104
123
|
|
|
105
124
|
try {
|
|
106
|
-
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
if (!force) {
|
|
126
|
+
const { confirmSandboxDeletePrompt: confirmed } = await promptUser([
|
|
127
|
+
{
|
|
128
|
+
name: 'confirmSandboxDeletePrompt',
|
|
129
|
+
type: 'confirm',
|
|
130
|
+
message: i18n(`${i18nKey}.confirm`, {
|
|
131
|
+
account: account || accountPrompt.account,
|
|
132
|
+
}),
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
135
|
+
if (!confirmed) {
|
|
136
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
137
|
+
}
|
|
117
138
|
}
|
|
118
139
|
|
|
119
140
|
await deleteSandbox(parentAccountId, sandboxAccountId);
|
|
@@ -133,8 +154,11 @@ exports.handler = async options => {
|
|
|
133
154
|
const promptDefaultAccount = removeSandboxAccountFromConfig(
|
|
134
155
|
sandboxAccountId
|
|
135
156
|
);
|
|
136
|
-
if (promptDefaultAccount) {
|
|
157
|
+
if (promptDefaultAccount && !force) {
|
|
137
158
|
await selectAndSetAsDefaultAccountPrompt(getConfig());
|
|
159
|
+
} else {
|
|
160
|
+
// If force is specified, skip prompt and set the parent account id as the default account
|
|
161
|
+
updateDefaultAccount(parentAccountId);
|
|
138
162
|
}
|
|
139
163
|
process.exit(EXIT_CODES.SUCCESS);
|
|
140
164
|
} catch (err) {
|
|
@@ -156,7 +180,7 @@ exports.handler = async options => {
|
|
|
156
180
|
) {
|
|
157
181
|
logger.log('');
|
|
158
182
|
logger.warn(
|
|
159
|
-
i18n(`${i18nKey}.objectNotFound`, {
|
|
183
|
+
i18n(`${i18nKey}.failure.objectNotFound`, {
|
|
160
184
|
account: account || accountPrompt.account,
|
|
161
185
|
})
|
|
162
186
|
);
|
|
@@ -165,8 +189,11 @@ exports.handler = async options => {
|
|
|
165
189
|
const promptDefaultAccount = removeSandboxAccountFromConfig(
|
|
166
190
|
sandboxAccountId
|
|
167
191
|
);
|
|
168
|
-
if (promptDefaultAccount) {
|
|
192
|
+
if (promptDefaultAccount && !force) {
|
|
169
193
|
await selectAndSetAsDefaultAccountPrompt(getConfig());
|
|
194
|
+
} else {
|
|
195
|
+
// If force is specified, skip prompt and set the parent account id as the default account
|
|
196
|
+
updateDefaultAccount(parentAccountId);
|
|
170
197
|
}
|
|
171
198
|
process.exit(EXIT_CODES.SUCCESS);
|
|
172
199
|
} else {
|
|
@@ -181,6 +208,11 @@ exports.builder = yargs => {
|
|
|
181
208
|
describe: i18n(`${i18nKey}.options.account.describe`),
|
|
182
209
|
type: 'string',
|
|
183
210
|
});
|
|
211
|
+
yargs.option('f', {
|
|
212
|
+
type: 'boolean',
|
|
213
|
+
alias: 'force',
|
|
214
|
+
describe: i18n(`${i18nKey}.examples.force`),
|
|
215
|
+
});
|
|
184
216
|
|
|
185
217
|
yargs.example([
|
|
186
218
|
[
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
const Spinnies = require('spinnies');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
|
|
4
|
-
const { logger } = require('@hubspot/cli-lib/logger');
|
|
5
2
|
|
|
6
3
|
const {
|
|
7
4
|
addConfigOptions,
|
|
@@ -12,16 +9,15 @@ const {
|
|
|
12
9
|
const { loadAndValidateOptions } = require('../../lib/validation');
|
|
13
10
|
const { trackCommandUsage } = require('../../lib/usageTracking');
|
|
14
11
|
const {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
kickOffValidation,
|
|
13
|
+
pollForValidationFinish,
|
|
14
|
+
fetchValidationResults,
|
|
15
|
+
processValidationErrors,
|
|
16
|
+
displayValidationResults,
|
|
17
|
+
} = require('../../lib/marketplace-validate');
|
|
20
18
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
21
19
|
|
|
22
20
|
const i18nKey = 'cli.commands.theme.subcommands.marketplaceValidate';
|
|
23
|
-
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
24
|
-
const SLEEP_TIME = 2000;
|
|
25
21
|
|
|
26
22
|
exports.command = 'marketplace-validate <src>';
|
|
27
23
|
exports.describe = i18n(`${i18nKey}.describe`);
|
|
@@ -36,130 +32,24 @@ exports.handler = async options => {
|
|
|
36
32
|
trackCommandUsage('validate', null, accountId);
|
|
37
33
|
|
|
38
34
|
const spinnies = new Spinnies();
|
|
39
|
-
|
|
40
35
|
spinnies.add('marketplaceValidation', {
|
|
41
36
|
text: i18n(`${i18nKey}.logs.validatingTheme`, {
|
|
42
37
|
path: src,
|
|
43
38
|
}),
|
|
44
39
|
});
|
|
45
40
|
|
|
46
|
-
// Kick off validation
|
|
47
|
-
let requestResult;
|
|
48
41
|
const assetType = 'THEME';
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
requestResult = await requestValidation(accountId, {
|
|
52
|
-
path: src,
|
|
53
|
-
assetType,
|
|
54
|
-
requestGroup,
|
|
55
|
-
});
|
|
56
|
-
} catch (err) {
|
|
57
|
-
logger.debug(err);
|
|
58
|
-
process.exit(EXIT_CODES.ERROR);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Poll till validation is finished
|
|
62
|
-
try {
|
|
63
|
-
const checkValidationStatus = async () => {
|
|
64
|
-
const validationStatus = await getValidationStatus(accountId, {
|
|
65
|
-
validationId: requestResult,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
if (validationStatus === 'REQUESTED') {
|
|
69
|
-
await new Promise(resolve => setTimeout(resolve, SLEEP_TIME));
|
|
70
|
-
await checkValidationStatus();
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
await checkValidationStatus();
|
|
75
|
-
|
|
76
|
-
spinnies.remove('marketplaceValidation');
|
|
77
|
-
} catch (err) {
|
|
78
|
-
logger.debug(err);
|
|
79
|
-
process.exit(EXIT_CODES.ERROR);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Fetch the validation results
|
|
83
|
-
let validationResults;
|
|
84
|
-
try {
|
|
85
|
-
validationResults = await getValidationResults(accountId, {
|
|
86
|
-
validationId: requestResult,
|
|
87
|
-
});
|
|
88
|
-
} catch (err) {
|
|
89
|
-
logger.debug(err);
|
|
90
|
-
process.exit(EXIT_CODES.ERROR);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (validationResults.errors.length) {
|
|
94
|
-
const { errors } = validationResults;
|
|
95
|
-
|
|
96
|
-
errors.forEach(err => {
|
|
97
|
-
logger.error(`${err.context}`);
|
|
98
|
-
});
|
|
99
|
-
process.exit(EXIT_CODES.ERROR);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const displayResults = checks => {
|
|
103
|
-
const displayFileInfo = (file, line) => {
|
|
104
|
-
if (file) {
|
|
105
|
-
logger.log(
|
|
106
|
-
i18n(`${i18nKey}.results.warnings.file`, {
|
|
107
|
-
file,
|
|
108
|
-
})
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
if (line) {
|
|
112
|
-
logger.log(
|
|
113
|
-
i18n(`${i18nKey}.results.warnings.lineNumber`, {
|
|
114
|
-
line,
|
|
115
|
-
})
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
return null;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
if (checks) {
|
|
122
|
-
const { status, results } = checks;
|
|
123
|
-
|
|
124
|
-
if (status === 'FAIL') {
|
|
125
|
-
const failedValidations = results.filter(
|
|
126
|
-
test => test.status === 'FAIL'
|
|
127
|
-
);
|
|
128
|
-
const warningValidations = results.filter(
|
|
129
|
-
test => test.status === 'WARN'
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
failedValidations.forEach(val => {
|
|
133
|
-
logger.error(`${val.message}`);
|
|
134
|
-
displayFileInfo(val.file, val.line);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
warningValidations.forEach(val => {
|
|
138
|
-
logger.warn(`${val.message}`);
|
|
139
|
-
displayFileInfo(val.file, val.line);
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (status === 'PASS') {
|
|
144
|
-
logger.success(i18n(`${i18nKey}.results.noErrors`));
|
|
42
|
+
const validationId = await kickOffValidation(accountId, assetType, src);
|
|
43
|
+
await pollForValidationFinish(accountId, validationId);
|
|
145
44
|
|
|
146
|
-
|
|
147
|
-
if (test.status === 'WARN') {
|
|
148
|
-
logger.warn(`${test.message}`);
|
|
149
|
-
displayFileInfo(test.file, test.line);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
155
|
-
};
|
|
45
|
+
spinnies.remove('marketplaceValidation');
|
|
156
46
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
47
|
+
const validationResults = await fetchValidationResults(
|
|
48
|
+
accountId,
|
|
49
|
+
validationId
|
|
50
|
+
);
|
|
51
|
+
processValidationErrors(validationResults);
|
|
52
|
+
displayValidationResults(i18nKey, validationResults);
|
|
163
53
|
|
|
164
54
|
process.exit();
|
|
165
55
|
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
4
|
+
const {
|
|
5
|
+
requestValidation,
|
|
6
|
+
getValidationStatus,
|
|
7
|
+
getValidationResults,
|
|
8
|
+
} = require('@hubspot/cli-lib/api/marketplaceValidation');
|
|
9
|
+
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
10
|
+
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
11
|
+
|
|
12
|
+
const SLEEP_TIME = 2000;
|
|
13
|
+
|
|
14
|
+
const kickOffValidation = async (accountId, assetType, src) => {
|
|
15
|
+
const requestGroup = 'EXTERNAL_DEVELOPER';
|
|
16
|
+
try {
|
|
17
|
+
const requestResult = await requestValidation(accountId, {
|
|
18
|
+
path: src,
|
|
19
|
+
assetType,
|
|
20
|
+
requestGroup,
|
|
21
|
+
});
|
|
22
|
+
return requestResult;
|
|
23
|
+
} catch (err) {
|
|
24
|
+
logger.debug(err);
|
|
25
|
+
process.exit(EXIT_CODES.ERROR);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const pollForValidationFinish = async (accountId, validationId) => {
|
|
30
|
+
try {
|
|
31
|
+
const checkValidationStatus = async () => {
|
|
32
|
+
const validationStatus = await getValidationStatus(accountId, {
|
|
33
|
+
validationId,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (validationStatus === 'REQUESTED') {
|
|
37
|
+
await new Promise(resolve => setTimeout(resolve, SLEEP_TIME));
|
|
38
|
+
await checkValidationStatus();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
await checkValidationStatus();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
logger.debug(err);
|
|
44
|
+
process.exit(EXIT_CODES.ERROR);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const fetchValidationResults = async (accountId, validationId) => {
|
|
49
|
+
try {
|
|
50
|
+
const validationResults = await getValidationResults(accountId, {
|
|
51
|
+
validationId,
|
|
52
|
+
});
|
|
53
|
+
return validationResults;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
logger.debug(err);
|
|
56
|
+
process.exit(EXIT_CODES.ERROR);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const processValidationErrors = validationResults => {
|
|
61
|
+
if (validationResults.errors.length) {
|
|
62
|
+
const { errors } = validationResults;
|
|
63
|
+
|
|
64
|
+
errors.forEach(err => {
|
|
65
|
+
logger.error(`${err.context}`);
|
|
66
|
+
});
|
|
67
|
+
process.exit(EXIT_CODES.ERROR);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const displayValidationResults = (i18nKey, validationResults) => {
|
|
72
|
+
const displayResults = checks => {
|
|
73
|
+
const displayFileInfo = (file, line) => {
|
|
74
|
+
if (file) {
|
|
75
|
+
logger.log(
|
|
76
|
+
i18n(`${i18nKey}.results.warnings.file`, {
|
|
77
|
+
file,
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (line) {
|
|
82
|
+
logger.log(
|
|
83
|
+
i18n(`${i18nKey}.results.warnings.lineNumber`, {
|
|
84
|
+
line,
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (checks) {
|
|
92
|
+
const { status, results } = checks;
|
|
93
|
+
|
|
94
|
+
if (status === 'FAIL') {
|
|
95
|
+
const failedValidations = results.filter(
|
|
96
|
+
test => test.status === 'FAIL'
|
|
97
|
+
);
|
|
98
|
+
const warningValidations = results.filter(
|
|
99
|
+
test => test.status === 'WARN'
|
|
100
|
+
);
|
|
101
|
+
failedValidations.forEach(val => {
|
|
102
|
+
logger.error(`${val.message}`);
|
|
103
|
+
displayFileInfo(val.file, val.line);
|
|
104
|
+
});
|
|
105
|
+
warningValidations.forEach(val => {
|
|
106
|
+
logger.warn(`${val.message}`);
|
|
107
|
+
displayFileInfo(val.file, val.line);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (status === 'PASS') {
|
|
111
|
+
logger.success(i18n(`${i18nKey}.results.noErrors`));
|
|
112
|
+
|
|
113
|
+
results.forEach(test => {
|
|
114
|
+
if (test.status === 'WARN') {
|
|
115
|
+
logger.warn(`${test.message}`);
|
|
116
|
+
displayFileInfo(test.file, test.line);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
Object.keys(validationResults.results).forEach(type => {
|
|
125
|
+
logger.log(chalk.bold(i18n(`${i18nKey}.results.${type.toLowerCase()}`)));
|
|
126
|
+
displayResults(validationResults.results[type]);
|
|
127
|
+
logger.log();
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
module.exports = {
|
|
132
|
+
kickOffValidation,
|
|
133
|
+
pollForValidationFinish,
|
|
134
|
+
fetchValidationResults,
|
|
135
|
+
processValidationErrors,
|
|
136
|
+
displayValidationResults,
|
|
137
|
+
};
|
|
@@ -15,6 +15,18 @@ const mapSandboxAccountChoices = portals =>
|
|
|
15
15
|
};
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
+
const mapNonSandboxAccountChoices = portals =>
|
|
19
|
+
portals
|
|
20
|
+
.filter(
|
|
21
|
+
p => p.sandboxAccountType === null || p.sandboxAccountType === undefined
|
|
22
|
+
)
|
|
23
|
+
.map(p => {
|
|
24
|
+
return {
|
|
25
|
+
name: `${p.name} (${p.portalId})`,
|
|
26
|
+
value: p.name || p.portalId,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
|
|
18
30
|
const createSandboxPrompt = () => {
|
|
19
31
|
return promptUser([
|
|
20
32
|
{
|
|
@@ -32,6 +44,12 @@ const createSandboxPrompt = () => {
|
|
|
32
44
|
};
|
|
33
45
|
|
|
34
46
|
const deleteSandboxPrompt = (config, promptParentAccount = false) => {
|
|
47
|
+
const choices = promptParentAccount
|
|
48
|
+
? mapNonSandboxAccountChoices(config.portals)
|
|
49
|
+
: mapSandboxAccountChoices(config.portals);
|
|
50
|
+
if (!choices.length) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
35
53
|
return promptUser([
|
|
36
54
|
{
|
|
37
55
|
name: 'account',
|
|
@@ -43,7 +61,7 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
|
|
|
43
61
|
type: 'list',
|
|
44
62
|
look: false,
|
|
45
63
|
pageSize: 20,
|
|
46
|
-
choices
|
|
64
|
+
choices,
|
|
47
65
|
default: config.defaultPortal,
|
|
48
66
|
},
|
|
49
67
|
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "4.1.7
|
|
3
|
+
"version": "4.1.7",
|
|
4
4
|
"description": "CLI for working with HubSpot",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"url": "https://github.com/HubSpot/hubspot-cms-tools"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@hubspot/cli-lib": "4.1.7
|
|
12
|
-
"@hubspot/serverless-dev-runtime": "4.1.7
|
|
11
|
+
"@hubspot/cli-lib": "4.1.7",
|
|
12
|
+
"@hubspot/serverless-dev-runtime": "4.1.7",
|
|
13
13
|
"archiver": "^5.3.0",
|
|
14
14
|
"chalk": "^4.1.2",
|
|
15
15
|
"cli-progress": "^3.11.2",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "ef2292568ec8308feb0c5841e4d3dd25a64d1050"
|
|
42
42
|
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
const marketplace = require('@hubspot/cli-lib/api/marketplace');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const ModuleDependencyValidator = require('../marketplaceValidators/module/ModuleDependencyValidator');
|
|
5
|
-
const { VALIDATION_RESULT } = require('../constants');
|
|
6
|
-
const { MODULE_PATH } = require('./validatorTestUtils');
|
|
7
|
-
|
|
8
|
-
jest.mock('@hubspot/cli-lib/api/marketplace');
|
|
9
|
-
|
|
10
|
-
const getMockDependencyResult = (customPaths = []) => {
|
|
11
|
-
const result = {
|
|
12
|
-
dependencies: [...customPaths],
|
|
13
|
-
};
|
|
14
|
-
return Promise.resolve(result);
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
describe('validators/marketplaceValidators/module/ModuleDependencyValidator', () => {
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
ModuleDependencyValidator.setRelativePath(MODULE_PATH);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('isExternalDep', () => {
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
ModuleDependencyValidator.setRelativePath(MODULE_PATH);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('returns true if dep is external to the provided absolute path', () => {
|
|
28
|
-
const isExternal = ModuleDependencyValidator.isExternalDep(
|
|
29
|
-
MODULE_PATH,
|
|
30
|
-
'SomeOtherFolder/In/The/DesignManager.css'
|
|
31
|
-
);
|
|
32
|
-
expect(isExternal).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('returns false if dep is not external to the provided absolute path', () => {
|
|
36
|
-
const isExternal = ModuleDependencyValidator.isExternalDep(
|
|
37
|
-
MODULE_PATH,
|
|
38
|
-
`${path.parse(MODULE_PATH).dir}/Internal/Folder/style.css`
|
|
39
|
-
);
|
|
40
|
-
expect(isExternal).toBe(false);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('validate', () => {
|
|
45
|
-
it('returns error if any referenced path is absolute', async () => {
|
|
46
|
-
marketplace.fetchModuleDependencies.mockReturnValue(
|
|
47
|
-
getMockDependencyResult(['/absolute/path'])
|
|
48
|
-
);
|
|
49
|
-
const validationErrors = await ModuleDependencyValidator.validate(
|
|
50
|
-
MODULE_PATH
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
expect(validationErrors.length).toBe(1);
|
|
54
|
-
expect(validationErrors[0].result).toBe(VALIDATION_RESULT.FATAL);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('returns error if any referenced path is external to the theme', async () => {
|
|
58
|
-
marketplace.fetchModuleDependencies.mockReturnValue(
|
|
59
|
-
getMockDependencyResult(['../../external/file-3.js'])
|
|
60
|
-
);
|
|
61
|
-
const validationErrors = await ModuleDependencyValidator.validate(
|
|
62
|
-
MODULE_PATH
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
expect(validationErrors.length).toBe(1);
|
|
66
|
-
expect(validationErrors[0].result).toBe(VALIDATION_RESULT.FATAL);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('returns no errors if paths are relative and internal', async () => {
|
|
70
|
-
marketplace.fetchModuleDependencies.mockReturnValue(
|
|
71
|
-
getMockDependencyResult(['module/style.css', 'module/another/test.js'])
|
|
72
|
-
);
|
|
73
|
-
const validationErrors = await ModuleDependencyValidator.validate(
|
|
74
|
-
MODULE_PATH
|
|
75
|
-
);
|
|
76
|
-
expect(validationErrors.length).toBe(0);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const ModuleValidator = require('../marketplaceValidators/module/ModuleValidator');
|
|
2
|
-
const { VALIDATION_RESULT } = require('../constants');
|
|
3
|
-
const { MODULE_PATH } = require('./validatorTestUtils');
|
|
4
|
-
const marketplace = require('@hubspot/cli-lib/api/marketplace');
|
|
5
|
-
|
|
6
|
-
jest.mock('@hubspot/cli-lib/api/marketplace');
|
|
7
|
-
|
|
8
|
-
describe('validators/marketplaceValidators/module/ModuleValidator', () => {
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
ModuleValidator.setRelativePath(MODULE_PATH);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('returns error if module meta.json file has invalid json', async () => {
|
|
14
|
-
marketplace.fetchModuleMeta.mockReturnValue(
|
|
15
|
-
Promise.resolve({ source: '{} bad json }' })
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const validationErrors = await ModuleValidator.validate(MODULE_PATH);
|
|
19
|
-
expect(validationErrors.length).toBe(1);
|
|
20
|
-
expect(validationErrors[0].result).toBe(VALIDATION_RESULT.FATAL);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('returns error if module meta.json file is missing a label field', async () => {
|
|
24
|
-
marketplace.fetchModuleMeta.mockReturnValue(
|
|
25
|
-
Promise.resolve({ source: '{ "icon": "woo" }' })
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
const validationErrors = await ModuleValidator.validate(MODULE_PATH);
|
|
29
|
-
expect(validationErrors.length).toBe(1);
|
|
30
|
-
expect(validationErrors[0].result).toBe(VALIDATION_RESULT.FATAL);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('returns error if module meta.json file is missing an icon field', async () => {
|
|
34
|
-
marketplace.fetchModuleMeta.mockReturnValue(
|
|
35
|
-
Promise.resolve({ source: '{ "label": "yay" }' })
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const validationErrors = await ModuleValidator.validate(MODULE_PATH);
|
|
39
|
-
expect(validationErrors.length).toBe(1);
|
|
40
|
-
expect(validationErrors[0].result).toBe(VALIDATION_RESULT.FATAL);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('returns no error if module meta.json file exists and has all required fields', async () => {
|
|
44
|
-
marketplace.fetchModuleMeta.mockReturnValue(
|
|
45
|
-
Promise.resolve({ source: '{ "label": "yay", "icon": "woo" }' })
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const validationErrors = await ModuleValidator.validate(MODULE_PATH);
|
|
49
|
-
expect(validationErrors.length).toBe(0);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
const RelativeValidator = require('../marketplaceValidators/RelativeValidator');
|
|
2
|
-
const { VALIDATION_RESULT } = require('../constants');
|
|
3
|
-
|
|
4
|
-
const Validator = new RelativeValidator({
|
|
5
|
-
name: 'Test validator',
|
|
6
|
-
key: 'validatorKey',
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
describe('validators/marketplaceValidators/RelativeValidator', () => {
|
|
10
|
-
it('getSuccess returns expected object', async () => {
|
|
11
|
-
const success = Validator.getSuccess();
|
|
12
|
-
|
|
13
|
-
expect(success.validatorKey).toBe('validatorKey');
|
|
14
|
-
expect(success.validatorName).toBe('Test validator');
|
|
15
|
-
expect(success.result).toBe(VALIDATION_RESULT.SUCCESS);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('getError returns expected object', async () => {
|
|
19
|
-
const errorObj = { key: 'errorkey', getCopy: () => 'Some error copy' };
|
|
20
|
-
const success = Validator.getError(errorObj);
|
|
21
|
-
|
|
22
|
-
expect(success.validatorKey).toBe('validatorKey');
|
|
23
|
-
expect(success.validatorName).toBe('Test validator');
|
|
24
|
-
expect(success.error).toBe('Some error copy');
|
|
25
|
-
expect(success.result).toBe(VALIDATION_RESULT.FATAL);
|
|
26
|
-
expect(success.key).toBe('validatorKey.errorkey');
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
async function applyRelativeValidators(validators, relativePath, ...args) {
|
|
2
|
-
return Promise.all(
|
|
3
|
-
validators.map(async Validator => {
|
|
4
|
-
Validator.setRelativePath(relativePath);
|
|
5
|
-
const validationResult = await Validator.validate(...args);
|
|
6
|
-
Validator.clearRelativePath();
|
|
7
|
-
|
|
8
|
-
if (!validationResult.length) {
|
|
9
|
-
// Return a success obj so we can log the successes
|
|
10
|
-
return [Validator.getSuccess()];
|
|
11
|
-
}
|
|
12
|
-
return validationResult;
|
|
13
|
-
})
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = { applyRelativeValidators };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const WARNING = 'WARNING';
|
|
2
|
-
const FATAL = 'FATAL';
|
|
3
|
-
const SUCCESS = 'SUCCESS';
|
|
4
|
-
|
|
5
|
-
const VALIDATION_RESULT = { WARNING, FATAL, SUCCESS };
|
|
6
|
-
const VALIDATOR_KEYS = {
|
|
7
|
-
module: 'module',
|
|
8
|
-
moduleDependency: 'moduleDependency',
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
module.exports = { VALIDATOR_KEYS, VALIDATION_RESULT };
|
package/lib/validators/index.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
const ModuleValidator = require('./marketplaceValidators/module/ModuleValidator');
|
|
2
|
-
const ModuleDependencyValidator = require('./marketplaceValidators/module/ModuleDependencyValidator');
|
|
3
|
-
|
|
4
|
-
const MARKETPLACE_VALIDATORS = {
|
|
5
|
-
module: [ModuleValidator, ModuleDependencyValidator],
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
module.exports = MARKETPLACE_VALIDATORS;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const chalk = require('chalk');
|
|
2
|
-
const { logger } = require('@hubspot/cli-lib/logger');
|
|
3
|
-
const { VALIDATION_RESULT } = require('./constants');
|
|
4
|
-
|
|
5
|
-
function logResultsAsJson(groupedResults) {
|
|
6
|
-
let success = true;
|
|
7
|
-
const resultObj = groupedResults.reduce((acc, resultList) => {
|
|
8
|
-
resultList.forEach(result => {
|
|
9
|
-
if (!acc[result.validatorKey]) {
|
|
10
|
-
if (success && result.result !== VALIDATION_RESULT.SUCCESS) {
|
|
11
|
-
success = false;
|
|
12
|
-
}
|
|
13
|
-
acc[result.validatorKey] = [];
|
|
14
|
-
}
|
|
15
|
-
const {
|
|
16
|
-
validatorName: __omit1, // eslint-disable-line no-unused-vars
|
|
17
|
-
validatorKey: __omit2, // eslint-disable-line no-unused-vars
|
|
18
|
-
...resultWithOmittedValues
|
|
19
|
-
} = result;
|
|
20
|
-
acc[result.validatorKey].push(resultWithOmittedValues);
|
|
21
|
-
});
|
|
22
|
-
return acc;
|
|
23
|
-
}, {});
|
|
24
|
-
const result = {
|
|
25
|
-
success,
|
|
26
|
-
results: resultObj,
|
|
27
|
-
};
|
|
28
|
-
// Use stdout here b/c console.log will truncate long strings
|
|
29
|
-
process.stdout.write(JSON.stringify(result) + '\n');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function logValidatorResults(groupedResults, { logAsJson = false } = {}) {
|
|
33
|
-
if (logAsJson) {
|
|
34
|
-
return logResultsAsJson(groupedResults);
|
|
35
|
-
}
|
|
36
|
-
groupedResults.forEach(results => {
|
|
37
|
-
logger.log(chalk.bold(`${results[0].validatorName} validation results:`));
|
|
38
|
-
logger.group();
|
|
39
|
-
results.forEach(({ error, result }) => {
|
|
40
|
-
switch (result) {
|
|
41
|
-
case VALIDATION_RESULT.WARNING:
|
|
42
|
-
logger.warn(error);
|
|
43
|
-
break;
|
|
44
|
-
case VALIDATION_RESULT.FATAL:
|
|
45
|
-
logger.error(error);
|
|
46
|
-
break;
|
|
47
|
-
case VALIDATION_RESULT.SUCCESS:
|
|
48
|
-
logger.success('No errors');
|
|
49
|
-
break;
|
|
50
|
-
default:
|
|
51
|
-
logger.log(error);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
logger.log('\n');
|
|
55
|
-
logger.groupEnd();
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
module.exports = { logValidatorResults };
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const { VALIDATION_RESULT } = require('../constants');
|
|
2
|
-
|
|
3
|
-
class RelativeValidator {
|
|
4
|
-
constructor({ name, key }) {
|
|
5
|
-
this.name = name;
|
|
6
|
-
this.key = key;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
clearRelativePath() {
|
|
10
|
-
this._relativePath = null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
setRelativePath(path) {
|
|
14
|
-
this._relativePath = path;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
getRelativePath() {
|
|
18
|
-
return this._relativePath;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
getSuccess() {
|
|
22
|
-
return {
|
|
23
|
-
validatorKey: this.key,
|
|
24
|
-
validatorName: this.name,
|
|
25
|
-
result: VALIDATION_RESULT.SUCCESS,
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
getError(errorObj, file, extraContext = {}) {
|
|
30
|
-
const relativeFilePath = this.getRelativePath();
|
|
31
|
-
const context = {
|
|
32
|
-
filePath: relativeFilePath,
|
|
33
|
-
...extraContext,
|
|
34
|
-
};
|
|
35
|
-
return {
|
|
36
|
-
validatorKey: this.key,
|
|
37
|
-
validatorName: this.name,
|
|
38
|
-
error: errorObj.getCopy(context),
|
|
39
|
-
result: errorObj.severity || VALIDATION_RESULT.FATAL,
|
|
40
|
-
key: `${this.key}.${errorObj.key}`,
|
|
41
|
-
context,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
module.exports = RelativeValidator;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
|
|
3
|
-
const { logger } = require('@hubspot/cli-lib/logger');
|
|
4
|
-
const { HUBSPOT_FOLDER } = require('@hubspot/cli-lib/lib/constants');
|
|
5
|
-
const { fetchModuleDependencies } = require('@hubspot/cli-lib/api/marketplace');
|
|
6
|
-
const { isRelativePath } = require('@hubspot/cli-lib/path');
|
|
7
|
-
|
|
8
|
-
const RelativeValidator = require('../RelativeValidator');
|
|
9
|
-
const { VALIDATOR_KEYS } = require('../../constants');
|
|
10
|
-
|
|
11
|
-
class ModuleDependencyValidator extends RelativeValidator {
|
|
12
|
-
constructor(options) {
|
|
13
|
-
super(options);
|
|
14
|
-
|
|
15
|
-
this.errors = {
|
|
16
|
-
FAILED_TO_FETCH_DEPS: {
|
|
17
|
-
key: 'failedDepFetch',
|
|
18
|
-
getCopy: ({ filePath }) =>
|
|
19
|
-
`Internal Error. Failed to fetch dependencies for ${filePath}. Please try again`,
|
|
20
|
-
},
|
|
21
|
-
EXTERNAL_DEPENDENCY: {
|
|
22
|
-
key: 'externalDependency',
|
|
23
|
-
getCopy: ({ filePath, referencedFilePath }) =>
|
|
24
|
-
`External dependency. ${filePath} references a file (${referencedFilePath}) that is outside of the module's immediate folder.`,
|
|
25
|
-
},
|
|
26
|
-
ABSOLUTE_DEPENDENCY_PATH: {
|
|
27
|
-
key: 'absoluteDependencyPath',
|
|
28
|
-
getCopy: ({ filePath, referencedFilePath }) =>
|
|
29
|
-
`Relative path required. ${filePath} references a file (${referencedFilePath}) using an absolute path`,
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
failedToFetchDependencies(err, relativePath, validationErrors) {
|
|
35
|
-
logger.debug(
|
|
36
|
-
`Failed to fetch dependencies for ${relativePath}: `,
|
|
37
|
-
err.error
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
validationErrors.push(
|
|
41
|
-
this.getError(this.errors.FAILED_TO_FETCH_DEPS, relativePath)
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async getAllDependenciesByPath(relativePath, accountId, validationErrors) {
|
|
46
|
-
let deps = [];
|
|
47
|
-
const file_deps = await fetchModuleDependencies(
|
|
48
|
-
accountId,
|
|
49
|
-
relativePath
|
|
50
|
-
).catch(err => {
|
|
51
|
-
this.failedToFetchDependencies(err, relativePath, validationErrors);
|
|
52
|
-
return null;
|
|
53
|
-
});
|
|
54
|
-
if (file_deps) {
|
|
55
|
-
deps = file_deps.dependencies || [];
|
|
56
|
-
}
|
|
57
|
-
return deps;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
isExternalDep(relPath, relativeDepPath) {
|
|
61
|
-
const moduleDir = path.parse(relPath).dir;
|
|
62
|
-
const depDir = path.parse(relativeDepPath).dir;
|
|
63
|
-
return !depDir.startsWith(moduleDir);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Validates:
|
|
67
|
-
// - Module does not contain external dependencies
|
|
68
|
-
// - All paths are either @hubspot or relative
|
|
69
|
-
async validate(relativePath, accountId) {
|
|
70
|
-
let validationErrors = [];
|
|
71
|
-
|
|
72
|
-
const dependencyData = await this.getAllDependenciesByPath(
|
|
73
|
-
relativePath,
|
|
74
|
-
accountId,
|
|
75
|
-
validationErrors
|
|
76
|
-
);
|
|
77
|
-
dependencyData.forEach(dependency => {
|
|
78
|
-
if (!dependency.startsWith(HUBSPOT_FOLDER)) {
|
|
79
|
-
if (!isRelativePath(dependency)) {
|
|
80
|
-
validationErrors.push(
|
|
81
|
-
this.getError(this.errors.ABSOLUTE_DEPENDENCY_PATH, relativePath, {
|
|
82
|
-
referencedFilePath: dependency,
|
|
83
|
-
})
|
|
84
|
-
);
|
|
85
|
-
} else if (this.isExternalDep(relativePath, dependency)) {
|
|
86
|
-
validationErrors.push(
|
|
87
|
-
this.getError(this.errors.EXTERNAL_DEPENDENCY, relativePath, {
|
|
88
|
-
referencedFilePath: dependency,
|
|
89
|
-
})
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
return validationErrors;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
module.exports = new ModuleDependencyValidator({
|
|
99
|
-
name: 'Module dependency',
|
|
100
|
-
key: VALIDATOR_KEYS.moduleDependency,
|
|
101
|
-
});
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
const { logger } = require('@hubspot/cli-lib/logger');
|
|
2
|
-
const { fetchModuleMeta } = require('@hubspot/cli-lib/api/marketplace');
|
|
3
|
-
const RelativeValidator = require('../RelativeValidator');
|
|
4
|
-
const { VALIDATOR_KEYS } = require('../../constants');
|
|
5
|
-
|
|
6
|
-
class ModuleValidator extends RelativeValidator {
|
|
7
|
-
constructor(options) {
|
|
8
|
-
super(options);
|
|
9
|
-
|
|
10
|
-
this.errors = {
|
|
11
|
-
FAILED_TO_FETCH_META_JSON: {
|
|
12
|
-
key: 'failedMetaFetch',
|
|
13
|
-
getCopy: ({ filePath }) =>
|
|
14
|
-
`Internal error. Failed to fetch meta.json for ${filePath}. Please try again.`,
|
|
15
|
-
},
|
|
16
|
-
MISSING_META_JSON: {
|
|
17
|
-
key: 'missingMetaJSON',
|
|
18
|
-
getCopy: ({ filePath }) =>
|
|
19
|
-
`Module ${filePath} is missing the meta.json file`,
|
|
20
|
-
},
|
|
21
|
-
INVALID_META_JSON: {
|
|
22
|
-
key: 'invalidMetaJSON',
|
|
23
|
-
getCopy: ({ filePath }) =>
|
|
24
|
-
`Module ${filePath} has invalid json in the meta.json file`,
|
|
25
|
-
},
|
|
26
|
-
MISSING_LABEL: {
|
|
27
|
-
key: 'missingLabel',
|
|
28
|
-
getCopy: ({ filePath }) =>
|
|
29
|
-
`Missing required property for ${filePath}. The meta.json file is missing the "label" property`,
|
|
30
|
-
},
|
|
31
|
-
MISSING_ICON: {
|
|
32
|
-
key: 'missingIcon',
|
|
33
|
-
getCopy: ({ filePath }) =>
|
|
34
|
-
`Missing required property for ${filePath}. The meta.json file is missing the "icon" property`,
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
failedToFetchDependencies(err, relativePath, validationErrors) {
|
|
39
|
-
logger.debug(
|
|
40
|
-
`Failed to fetch dependencies for ${relativePath}: `,
|
|
41
|
-
err.error
|
|
42
|
-
);
|
|
43
|
-
validationErrors.push(
|
|
44
|
-
this.getError(this.errors.FAILED_TO_FETCH_META_JSON, relativePath)
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async getModuleMetaByPath(relativePath, accountId, validationErrors) {
|
|
49
|
-
const moduleMeta = await fetchModuleMeta(accountId, relativePath).catch(
|
|
50
|
-
err => {
|
|
51
|
-
this.failedToFetchDependencies(err, relativePath, validationErrors);
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
);
|
|
55
|
-
return moduleMeta;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Validates:
|
|
59
|
-
// - Module folder contains a meta.json file
|
|
60
|
-
// - Module meta.json file contains valid json
|
|
61
|
-
// - Module meta.json file has a "label" field
|
|
62
|
-
// - Module meta.json file has an "icon" field
|
|
63
|
-
async validate(relativePath, accountId) {
|
|
64
|
-
let validationErrors = [];
|
|
65
|
-
const metaJSONFile = await this.getModuleMetaByPath(
|
|
66
|
-
relativePath,
|
|
67
|
-
accountId,
|
|
68
|
-
validationErrors
|
|
69
|
-
);
|
|
70
|
-
if (!metaJSONFile) {
|
|
71
|
-
validationErrors.push(
|
|
72
|
-
this.getError(this.errors.MISSING_META_JSON, relativePath)
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
let metaJSON;
|
|
76
|
-
try {
|
|
77
|
-
metaJSON = JSON.parse(metaJSONFile.source);
|
|
78
|
-
} catch (err) {
|
|
79
|
-
validationErrors.push(
|
|
80
|
-
this.getError(this.errors.INVALID_META_JSON, relativePath)
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
if (metaJSON) {
|
|
84
|
-
if (!metaJSON.label) {
|
|
85
|
-
validationErrors.push(
|
|
86
|
-
this.getError(this.errors.MISSING_LABEL, relativePath)
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (!metaJSON.icon) {
|
|
90
|
-
validationErrors.push(
|
|
91
|
-
this.getError(this.errors.MISSING_ICON, relativePath)
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return validationErrors;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
module.exports = new ModuleValidator({
|
|
100
|
-
name: 'Module',
|
|
101
|
-
key: VALIDATOR_KEYS.module,
|
|
102
|
-
});
|