@hubspot/cli 5.0.2-beta.0 → 5.0.2
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/bin/cli.js +1 -1
- package/commands/accounts/clean.js +1 -1
- package/commands/auth.js +1 -1
- package/commands/config/set/defaultMode.js +1 -1
- package/commands/create.js +1 -1
- package/commands/customObject/create.js +1 -1
- package/commands/customObject/schema/create.js +3 -1
- package/commands/customObject/schema/delete.js +3 -1
- package/commands/customObject/schema/fetch-all.js +3 -1
- package/commands/customObject/schema/fetch.js +3 -1
- package/commands/customObject/schema/list.js +4 -2
- package/commands/customObject/schema/update.js +3 -1
- package/commands/filemanager/upload.js +2 -2
- package/commands/functions/deploy.js +1 -1
- package/commands/functions/list.js +2 -2
- package/commands/hubdb/clear.js +1 -1
- package/commands/hubdb/create.js +1 -1
- package/commands/hubdb/delete.js +1 -1
- package/commands/hubdb/fetch.js +1 -1
- package/commands/init.js +2 -2
- package/commands/lint.js +3 -5
- package/commands/list.js +1 -1
- package/commands/mv.js +1 -1
- package/commands/project/add.js +1 -1
- package/commands/project/create.js +1 -0
- package/commands/project/deploy.js +1 -1
- package/commands/project/dev.js +6 -7
- package/commands/project/download.js +1 -1
- package/commands/project/listBuilds.js +1 -1
- package/commands/project/logs.js +1 -1
- package/commands/project/upload.js +2 -4
- package/commands/project/watch.js +3 -3
- package/commands/remove.js +1 -1
- package/commands/sandbox/create.js +2 -4
- package/commands/sandbox/delete.js +7 -6
- package/commands/sandbox/sync.js +2 -6
- package/commands/secrets/addSecret.js +1 -1
- package/commands/secrets/deleteSecret.js +1 -1
- package/commands/secrets/listSecrets.js +1 -1
- package/commands/secrets/updateSecret.js +1 -1
- package/commands/upload.js +2 -2
- package/lang/en.lyaml +57 -2
- package/lib/LocalDevManager.js +7 -2
- package/lib/errorHandlers/apiErrors.js +352 -0
- package/lib/errorHandlers/fileSystemErrors.js +55 -0
- package/lib/errorHandlers/standardErrors.js +95 -0
- package/lib/getFunctionArrays.js +18 -0
- package/lib/hublValidate.js +32 -0
- package/lib/oauth.js +1 -1
- package/lib/process.js +42 -0
- package/lib/projects.js +24 -8
- package/lib/projectsWatch.js +225 -0
- package/lib/prompts/downloadProjectPrompt.js +1 -1
- package/lib/prompts/projectsLogsPrompt.js +1 -1
- package/lib/sandbox-create.js +2 -2
- package/lib/sandbox-sync.js +5 -5
- package/lib/sandboxes.js +1 -1
- package/lib/schema.js +31 -0
- package/lib/serverlessLogs.js +2 -2
- package/lib/ui.js +1 -0
- package/lib/validation.js +1 -1
- package/package.json +7 -4
package/lib/LocalDevManager.js
CHANGED
|
@@ -2,8 +2,8 @@ const path = require('path');
|
|
|
2
2
|
const chokidar = require('chokidar');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const { i18n } = require('./lang');
|
|
5
|
+
const { handleKeypress } = require('./process');
|
|
5
6
|
const { logger } = require('@hubspot/cli-lib/logger');
|
|
6
|
-
const { handleKeypress } = require('@hubspot/cli-lib/lib/process');
|
|
7
7
|
const {
|
|
8
8
|
getAccountId,
|
|
9
9
|
getConfigDefaultAccount,
|
|
@@ -89,7 +89,12 @@ class LocalDevManager {
|
|
|
89
89
|
|
|
90
90
|
// The project does not contain any components that support local development
|
|
91
91
|
if (!runnableComponents.length) {
|
|
92
|
-
logger.error(
|
|
92
|
+
logger.error(
|
|
93
|
+
i18n(`${i18nKey}.noRunnableComponents`, {
|
|
94
|
+
projectSourceDir: this.projectSourceDir,
|
|
95
|
+
command: uiCommandReference('hs project add'),
|
|
96
|
+
})
|
|
97
|
+
);
|
|
93
98
|
process.exit(EXIT_CODES.SUCCESS);
|
|
94
99
|
}
|
|
95
100
|
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
2
|
+
const { getAccountConfig } = require('@hubspot/cli-lib/lib/config');
|
|
3
|
+
const {
|
|
4
|
+
SCOPE_GROUPS,
|
|
5
|
+
PERSONAL_ACCESS_KEY_AUTH_METHOD,
|
|
6
|
+
} = require('@hubspot/cli-lib/lib/constants');
|
|
7
|
+
const {
|
|
8
|
+
fetchScopeData,
|
|
9
|
+
} = require('@hubspot/cli-lib/api/localDevAuth/authenticated');
|
|
10
|
+
const {
|
|
11
|
+
debugErrorAndContext,
|
|
12
|
+
logErrorInstance,
|
|
13
|
+
ErrorContext,
|
|
14
|
+
} = require('./standardErrors');
|
|
15
|
+
const { i18n } = require('../lang');
|
|
16
|
+
|
|
17
|
+
const i18nKey = 'cli.lib.errorHandlers.apiErrors';
|
|
18
|
+
|
|
19
|
+
const isApiStatusCodeError = err =>
|
|
20
|
+
err.name === 'StatusCodeError' ||
|
|
21
|
+
(err.statusCode >= 100 && err.statusCode < 600);
|
|
22
|
+
|
|
23
|
+
const isApiUploadValidationError = err =>
|
|
24
|
+
!!(
|
|
25
|
+
err.statusCode === 400 &&
|
|
26
|
+
err.response &&
|
|
27
|
+
err.response.body &&
|
|
28
|
+
(err.response.body.message || err.response.body.errors)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const isMissingScopeError = err =>
|
|
32
|
+
err.name === 'StatusCodeError' &&
|
|
33
|
+
err.statusCode === 403 &&
|
|
34
|
+
err.error &&
|
|
35
|
+
err.error.category === 'MISSING_SCOPES';
|
|
36
|
+
|
|
37
|
+
const isGatingError = err =>
|
|
38
|
+
isSpecifiedError(err, { statusCode: 403, category: 'GATED' });
|
|
39
|
+
|
|
40
|
+
const isSpecifiedError = (err, { statusCode, category, subCategory } = {}) => {
|
|
41
|
+
const statusCodeErr = !statusCode || err.statusCode === statusCode;
|
|
42
|
+
const categoryErr =
|
|
43
|
+
!category || (err.error && err.error.category === category);
|
|
44
|
+
const subCategoryErr =
|
|
45
|
+
!subCategory || (err.error && err.error.subCategory === subCategory);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
err.name === 'StatusCodeError' &&
|
|
49
|
+
statusCodeErr &&
|
|
50
|
+
categoryErr &&
|
|
51
|
+
subCategoryErr
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const isSpecifiedHubSpotAuthError = (
|
|
56
|
+
err,
|
|
57
|
+
{ statusCode, category, subCategory }
|
|
58
|
+
) => {
|
|
59
|
+
const statusCodeErr = !statusCode || err.statusCode === statusCode;
|
|
60
|
+
const categoryErr = !category || err.category === category;
|
|
61
|
+
const subCategoryErr = !subCategory || err.subCategory === subCategory;
|
|
62
|
+
return (
|
|
63
|
+
err.name === 'HubSpotAuthError' &&
|
|
64
|
+
statusCodeErr &&
|
|
65
|
+
categoryErr &&
|
|
66
|
+
subCategoryErr
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const parseValidationErrors = (responseBody = {}) => {
|
|
71
|
+
const errorMessages = [];
|
|
72
|
+
|
|
73
|
+
const { errors, message } = responseBody;
|
|
74
|
+
|
|
75
|
+
if (message) {
|
|
76
|
+
errorMessages.push(message);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (errors) {
|
|
80
|
+
const specificErrors = errors.map(error => {
|
|
81
|
+
let errorMessage = error.message;
|
|
82
|
+
if (error.errorTokens && error.errorTokens.line) {
|
|
83
|
+
errorMessage = `line ${error.errorTokens.line}: ${errorMessage}`;
|
|
84
|
+
}
|
|
85
|
+
return errorMessage;
|
|
86
|
+
});
|
|
87
|
+
errorMessages.push(...specificErrors);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return errorMessages;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
class ApiErrorContext extends ErrorContext {
|
|
94
|
+
constructor(props = {}) {
|
|
95
|
+
super(props);
|
|
96
|
+
/** @type {string} */
|
|
97
|
+
this.request = props.request || '';
|
|
98
|
+
/** @type {string} */
|
|
99
|
+
this.payload = props.payload || '';
|
|
100
|
+
/** @type {string} */
|
|
101
|
+
this.projectName = props.projectName || '';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {Error} error
|
|
107
|
+
* @param {ApiErrorContext} context
|
|
108
|
+
*/
|
|
109
|
+
function logValidationErrors(error, context) {
|
|
110
|
+
const { response = {} } = error;
|
|
111
|
+
const validationErrors = parseValidationErrors(response.body);
|
|
112
|
+
if (validationErrors.length) {
|
|
113
|
+
validationErrors.forEach(err => {
|
|
114
|
+
logger.error(err);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
debugErrorAndContext(error, context);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Message segments for API messages.
|
|
122
|
+
*
|
|
123
|
+
* @enum {string}
|
|
124
|
+
*/
|
|
125
|
+
const ApiMethodVerbs = {
|
|
126
|
+
DEFAULT: 'request',
|
|
127
|
+
DELETE: 'delete',
|
|
128
|
+
GET: 'request',
|
|
129
|
+
PATCH: 'update',
|
|
130
|
+
POST: 'post',
|
|
131
|
+
PUT: 'update',
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Message segments for API messages.
|
|
136
|
+
*
|
|
137
|
+
* @enum {string}
|
|
138
|
+
*/
|
|
139
|
+
const ApiMethodPrepositions = {
|
|
140
|
+
DEFAULT: 'for',
|
|
141
|
+
DELETE: 'of',
|
|
142
|
+
GET: 'for',
|
|
143
|
+
PATCH: 'to',
|
|
144
|
+
POST: 'to',
|
|
145
|
+
PUT: 'to',
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Logs messages for an error instance resulting from API interaction.
|
|
150
|
+
*
|
|
151
|
+
* @param {StatusCodeError} error
|
|
152
|
+
* @param {ApiErrorContext} context
|
|
153
|
+
*/
|
|
154
|
+
function logApiStatusCodeError(error, context) {
|
|
155
|
+
const { statusCode } = error;
|
|
156
|
+
const { method } = error.options || {};
|
|
157
|
+
const { projectName } = context;
|
|
158
|
+
const isPutOrPost = method === 'PUT' || method === 'POST';
|
|
159
|
+
const action = ApiMethodVerbs[method] || ApiMethodVerbs.DEFAULT;
|
|
160
|
+
const preposition =
|
|
161
|
+
ApiMethodPrepositions[method] || ApiMethodPrepositions.DEFAULT;
|
|
162
|
+
let messageDetail = '';
|
|
163
|
+
{
|
|
164
|
+
const request = context.request
|
|
165
|
+
? `${action} ${preposition} "${context.request}"`
|
|
166
|
+
: action;
|
|
167
|
+
messageDetail = `${request} in account ${context.accountId}`;
|
|
168
|
+
}
|
|
169
|
+
const errorMessage = [];
|
|
170
|
+
if (isPutOrPost && context.payload) {
|
|
171
|
+
errorMessage.push(`Unable to upload "${context.payload}".`);
|
|
172
|
+
}
|
|
173
|
+
const isProjectMissingScopeError = isMissingScopeError(error) && projectName;
|
|
174
|
+
const isProjectGatingError = isGatingError(error) && projectName;
|
|
175
|
+
switch (statusCode) {
|
|
176
|
+
case 400:
|
|
177
|
+
errorMessage.push(i18n(`${i18nKey}.codes.400`, { messageDetail }));
|
|
178
|
+
break;
|
|
179
|
+
case 401:
|
|
180
|
+
errorMessage.push(i18n(`${i18nKey}.codes.401`, { messageDetail }));
|
|
181
|
+
break;
|
|
182
|
+
case 403:
|
|
183
|
+
if (isProjectMissingScopeError) {
|
|
184
|
+
errorMessage.push(
|
|
185
|
+
i18n(`${i18nKey}.codes.403MissingScope`, {
|
|
186
|
+
accountId: context.accountId || '',
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
} else if (isProjectGatingError) {
|
|
190
|
+
errorMessage.push(
|
|
191
|
+
i18n(`${i18nKey}.codes.403Gating`, {
|
|
192
|
+
accountId: context.accountId || '',
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
} else {
|
|
196
|
+
errorMessage.push(i18n(`${i18nKey}.codes.403`, { messageDetail }));
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
case 404:
|
|
200
|
+
if (context.request) {
|
|
201
|
+
errorMessage.push(
|
|
202
|
+
i18n(`${i18nKey}.codes.404Request`, {
|
|
203
|
+
action: action || 'request',
|
|
204
|
+
request: context.request,
|
|
205
|
+
account: context.accountId || '',
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
} else {
|
|
209
|
+
errorMessage.push(i18n(`${i18nKey}.codes.404`, { messageDetail }));
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
case 429:
|
|
213
|
+
errorMessage.push(i18n(`${i18nKey}.codes.429`, { messageDetail }));
|
|
214
|
+
break;
|
|
215
|
+
case 503:
|
|
216
|
+
errorMessage.push(i18n(`${i18nKey}.codes.503`, { messageDetail }));
|
|
217
|
+
break;
|
|
218
|
+
default:
|
|
219
|
+
if (statusCode >= 500 && statusCode < 600) {
|
|
220
|
+
errorMessage.push(
|
|
221
|
+
i18n(`${i18nKey}.codes.500Generic`, { messageDetail })
|
|
222
|
+
);
|
|
223
|
+
} else if (statusCode >= 400 && statusCode < 500) {
|
|
224
|
+
i18n(`${i18nKey}.codes.400Generic`, { messageDetail });
|
|
225
|
+
} else {
|
|
226
|
+
errorMessage.push(i18n(`${i18nKey}.codes.generic`, { messageDetail }));
|
|
227
|
+
}
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
if (
|
|
231
|
+
error.error &&
|
|
232
|
+
error.error.message &&
|
|
233
|
+
!isProjectMissingScopeError &&
|
|
234
|
+
!isProjectGatingError
|
|
235
|
+
) {
|
|
236
|
+
errorMessage.push(error.error.message);
|
|
237
|
+
}
|
|
238
|
+
if (error.error && error.error.errors) {
|
|
239
|
+
error.error.errors.forEach(err => {
|
|
240
|
+
errorMessage.push('\n- ' + err.message);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
logger.error(errorMessage.join(' '));
|
|
244
|
+
debugErrorAndContext(error, context);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Logs a message for an error instance resulting from API interaction.
|
|
249
|
+
*
|
|
250
|
+
* @param {Error|SystemError|Object} error
|
|
251
|
+
* @param {ApiErrorContext} context
|
|
252
|
+
*/
|
|
253
|
+
function logApiErrorInstance(error, context) {
|
|
254
|
+
// StatusCodeError
|
|
255
|
+
if (isApiStatusCodeError(error)) {
|
|
256
|
+
logApiStatusCodeError(error, context);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
logErrorInstance(error, context);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Logs a message for an error instance resulting from filemapper API upload.
|
|
264
|
+
*
|
|
265
|
+
* @param {Error|SystemError|Object} error
|
|
266
|
+
* @param {ApiErrorContext} context
|
|
267
|
+
*/
|
|
268
|
+
function logApiUploadErrorInstance(error, context) {
|
|
269
|
+
if (isApiUploadValidationError(error)) {
|
|
270
|
+
logValidationErrors(error, context);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
logApiErrorInstance(error, context);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function verifyAccessKeyAndUserAccess(accountId, scopeGroup) {
|
|
277
|
+
const accountConfig = getAccountConfig(accountId);
|
|
278
|
+
const { authType } = accountConfig;
|
|
279
|
+
if (authType !== PERSONAL_ACCESS_KEY_AUTH_METHOD.value) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let scopesData;
|
|
284
|
+
try {
|
|
285
|
+
scopesData = await fetchScopeData(accountId, scopeGroup);
|
|
286
|
+
} catch (e) {
|
|
287
|
+
logger.debug(
|
|
288
|
+
i18n(`${i18nKey}.verifyAccessKeyAndUserAccess.fetchScopeDataError`, {
|
|
289
|
+
scopeGroup,
|
|
290
|
+
error: e,
|
|
291
|
+
})
|
|
292
|
+
);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const { portalScopesInGroup, userScopesInGroup } = scopesData;
|
|
296
|
+
|
|
297
|
+
if (!portalScopesInGroup.length) {
|
|
298
|
+
logger.error(
|
|
299
|
+
i18n(`${i18nKey}.verifyAccessKeyAndUserAccess.portalMissingScope`)
|
|
300
|
+
);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (!portalScopesInGroup.every(s => userScopesInGroup.includes(s))) {
|
|
305
|
+
logger.error(
|
|
306
|
+
i18n(`${i18nKey}.verifyAccessKeyAndUserAccess.userMissingScope`)
|
|
307
|
+
);
|
|
308
|
+
return;
|
|
309
|
+
} else {
|
|
310
|
+
logger.error(
|
|
311
|
+
i18n(`${i18nKey}.verifyAccessKeyAndUserAccess.genericMissingScope`)
|
|
312
|
+
);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Logs a message for an error instance resulting from API interaction
|
|
319
|
+
* related to serverless function.
|
|
320
|
+
*
|
|
321
|
+
* @param {int} accountId
|
|
322
|
+
* @param {Error|SystemError|Object} error
|
|
323
|
+
* @param {ApiErrorContext} context
|
|
324
|
+
*/
|
|
325
|
+
async function logServerlessFunctionApiErrorInstance(
|
|
326
|
+
accountId,
|
|
327
|
+
error,
|
|
328
|
+
context
|
|
329
|
+
) {
|
|
330
|
+
if (isMissingScopeError(error)) {
|
|
331
|
+
await verifyAccessKeyAndUserAccess(accountId, SCOPE_GROUPS.functions);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// StatusCodeError
|
|
336
|
+
if (isApiStatusCodeError(error)) {
|
|
337
|
+
logApiStatusCodeError(error, context);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
logErrorInstance(error, context);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
module.exports = {
|
|
344
|
+
ApiErrorContext,
|
|
345
|
+
parseValidationErrors,
|
|
346
|
+
logApiErrorInstance,
|
|
347
|
+
logApiUploadErrorInstance,
|
|
348
|
+
logServerlessFunctionApiErrorInstance,
|
|
349
|
+
isMissingScopeError,
|
|
350
|
+
isSpecifiedError,
|
|
351
|
+
isSpecifiedHubSpotAuthError,
|
|
352
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
2
|
+
const {
|
|
3
|
+
ErrorContext,
|
|
4
|
+
isSystemError,
|
|
5
|
+
debugErrorAndContext,
|
|
6
|
+
} = require('./standardErrors');
|
|
7
|
+
const { i18n } = require('../lang');
|
|
8
|
+
|
|
9
|
+
const i18nKey = 'cli.lib.errorHandlers.fileSystemErrors';
|
|
10
|
+
|
|
11
|
+
class FileSystemErrorContext extends ErrorContext {
|
|
12
|
+
constructor(props = {}) {
|
|
13
|
+
super(props);
|
|
14
|
+
/** @type {string} */
|
|
15
|
+
this.filepath = props.filepath || '';
|
|
16
|
+
/** @type {boolean} */
|
|
17
|
+
this.read = !!props.read;
|
|
18
|
+
/** @type {boolean} */
|
|
19
|
+
this.write = !!props.write;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Logs a message for an error instance resulting from filesystem interaction.
|
|
25
|
+
*
|
|
26
|
+
* @param {Error|SystemError|Object} error
|
|
27
|
+
* @param {FileSystemErrorContext} context
|
|
28
|
+
*/
|
|
29
|
+
function logFileSystemErrorInstance(error, context) {
|
|
30
|
+
let fileAction = '';
|
|
31
|
+
if (context.read) {
|
|
32
|
+
fileAction = 'reading from';
|
|
33
|
+
} else if (context.write) {
|
|
34
|
+
fileAction = 'writing to';
|
|
35
|
+
} else {
|
|
36
|
+
fileAction = 'accessing';
|
|
37
|
+
}
|
|
38
|
+
const filepath = context.filepath
|
|
39
|
+
? `"${context.filepath}"`
|
|
40
|
+
: 'a file or folder';
|
|
41
|
+
const message = [i18n(`${i18nKey}.errorOccurred`, { fileAction, filepath })];
|
|
42
|
+
// Many `fs` errors will be `SystemError`s
|
|
43
|
+
if (isSystemError(error)) {
|
|
44
|
+
message.push(
|
|
45
|
+
i18n(`${i18nKey}.errorExplanation`, { errorMessage: error.message })
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
logger.error(message.join(' '));
|
|
49
|
+
debugErrorAndContext(error, context);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
FileSystemErrorContext,
|
|
54
|
+
logFileSystemErrorInstance,
|
|
55
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const { HubSpotAuthError } = require('@hubspot/cli-lib/lib/models/Errors');
|
|
2
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
3
|
+
const { i18n } = require('../lang');
|
|
4
|
+
|
|
5
|
+
const i18nKey = 'cli.lib.errorHandlers.standardErrors';
|
|
6
|
+
|
|
7
|
+
const isSystemError = err =>
|
|
8
|
+
err.errno != null && err.code != null && err.syscall != null;
|
|
9
|
+
const isFatalError = err => err instanceof HubSpotAuthError;
|
|
10
|
+
|
|
11
|
+
// TODO: Make these TS interfaces
|
|
12
|
+
class ErrorContext {
|
|
13
|
+
constructor(props = {}) {
|
|
14
|
+
/** @type {number} */
|
|
15
|
+
this.accountId = props.accountId;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Logs (debug) the error and context objects.
|
|
21
|
+
*
|
|
22
|
+
* @param {SystemError} error
|
|
23
|
+
* @param {ErrorContext} context
|
|
24
|
+
*/
|
|
25
|
+
function debugErrorAndContext(error, context) {
|
|
26
|
+
if (error.name === 'StatusCodeError') {
|
|
27
|
+
const { statusCode, message, response } = error;
|
|
28
|
+
logger.debug(
|
|
29
|
+
i18n(`${i18nKey}.errorOccurred`, {
|
|
30
|
+
error: {
|
|
31
|
+
statusCode,
|
|
32
|
+
message,
|
|
33
|
+
url: response.request.href,
|
|
34
|
+
method: response.request.method,
|
|
35
|
+
response: response.body,
|
|
36
|
+
headers: response.headers,
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
} else {
|
|
41
|
+
logger.debug(i18n(`${i18nKey}.errorOccurred`, { error }));
|
|
42
|
+
}
|
|
43
|
+
logger.debug(i18n(`${i18nKey}.errorContext`, { context }));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Logs a SystemError
|
|
48
|
+
* @see {@link https://nodejs.org/api/errors.html#errors_class_systemerror}
|
|
49
|
+
*
|
|
50
|
+
* @param {SystemError} error
|
|
51
|
+
* @param {ErrorContext} context
|
|
52
|
+
*/
|
|
53
|
+
function logSystemError(error, context) {
|
|
54
|
+
logger.error(
|
|
55
|
+
i18n(`${i18nKey}.systemErrorOccurred`, { error: error.message })
|
|
56
|
+
);
|
|
57
|
+
debugErrorAndContext(error, context);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Logs a message for an error instance of type not asserted.
|
|
62
|
+
*
|
|
63
|
+
* @param {Error|SystemError|Object} error
|
|
64
|
+
* @param {ErrorContext} context
|
|
65
|
+
*/
|
|
66
|
+
function logErrorInstance(error, context) {
|
|
67
|
+
// SystemError
|
|
68
|
+
if (isSystemError(error)) {
|
|
69
|
+
logSystemError(error, context);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (error instanceof Error || error.message || error.reason) {
|
|
73
|
+
// Error or Error subclass
|
|
74
|
+
const name = error.name || 'Error';
|
|
75
|
+
const message = [i18n(`${i18nKey}.genericErrorOccurred`, { name })];
|
|
76
|
+
[(error.message, error.reason)].forEach(msg => {
|
|
77
|
+
if (msg) {
|
|
78
|
+
message.push(msg);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
logger.error(message.join(' '));
|
|
82
|
+
} else {
|
|
83
|
+
// Unknown errors
|
|
84
|
+
logger.error(i18n(`${i18nKey}.unknownErrorOccurred`));
|
|
85
|
+
}
|
|
86
|
+
debugErrorAndContext(error, context);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
debugErrorAndContext,
|
|
91
|
+
ErrorContext,
|
|
92
|
+
isFatalError,
|
|
93
|
+
isSystemError,
|
|
94
|
+
logErrorInstance,
|
|
95
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const moment = require('moment');
|
|
2
|
+
|
|
3
|
+
const getFunctionArrays = resp => {
|
|
4
|
+
return resp.objects.map(func => {
|
|
5
|
+
const { route, method, created, updated, secretNames } = func;
|
|
6
|
+
return [
|
|
7
|
+
route,
|
|
8
|
+
method,
|
|
9
|
+
secretNames.join(', '),
|
|
10
|
+
moment(created).format(),
|
|
11
|
+
moment(updated).format(),
|
|
12
|
+
];
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
getFunctionArrays,
|
|
18
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
2
|
+
|
|
3
|
+
const getErrorsFromHublValidationObject = validation =>
|
|
4
|
+
(validation && validation.meta && validation.meta.template_errors) || [];
|
|
5
|
+
|
|
6
|
+
function printHublValidationError(err) {
|
|
7
|
+
const { severity, message, lineno, startPosition } = err;
|
|
8
|
+
const method = severity === 'FATAL' ? 'error' : 'warn';
|
|
9
|
+
logger[method]('[%d, %d]: %s', lineno, startPosition, message);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function printHublValidationResult({ file, validation }) {
|
|
13
|
+
let count = 0;
|
|
14
|
+
const errors = getErrorsFromHublValidationObject(validation);
|
|
15
|
+
if (!errors.length) {
|
|
16
|
+
return count;
|
|
17
|
+
}
|
|
18
|
+
logger.group(file);
|
|
19
|
+
errors.forEach(err => {
|
|
20
|
+
if (err.reason !== 'SYNTAX_ERROR') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
++count;
|
|
24
|
+
printHublValidationError(err);
|
|
25
|
+
});
|
|
26
|
+
logger.groupEnd(file);
|
|
27
|
+
return count;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
printHublValidationResult,
|
|
32
|
+
};
|
package/lib/oauth.js
CHANGED
|
@@ -3,7 +3,7 @@ const open = require('open');
|
|
|
3
3
|
const OAuth2Manager = require('@hubspot/cli-lib/lib/models/OAuth2Manager');
|
|
4
4
|
const { getAccountConfig } = require('@hubspot/cli-lib/lib/config');
|
|
5
5
|
const { addOauthToAccountConfig } = require('@hubspot/cli-lib/oauth');
|
|
6
|
-
const { handleExit } = require('
|
|
6
|
+
const { handleExit } = require('./process');
|
|
7
7
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
|
|
8
8
|
const { logger } = require('@hubspot/cli-lib/logger');
|
|
9
9
|
const { ENVIRONMENTS } = require('@hubspot/cli-lib/lib/constants');
|
package/lib/process.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
|
|
3
|
+
const handleExit = callback => {
|
|
4
|
+
const terminationSignals = [
|
|
5
|
+
'beforeExit',
|
|
6
|
+
'SIGINT',
|
|
7
|
+
'SIGUSR1',
|
|
8
|
+
'SIGUSR2',
|
|
9
|
+
'uncaughtException',
|
|
10
|
+
'SIGTERM',
|
|
11
|
+
'SIGHUP',
|
|
12
|
+
];
|
|
13
|
+
terminationSignals.forEach(signal => {
|
|
14
|
+
process.removeAllListeners(signal);
|
|
15
|
+
|
|
16
|
+
process.on(signal, async () => {
|
|
17
|
+
await callback();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const handleKeypress = callback => {
|
|
23
|
+
readline.createInterface(process.stdin, process.stdout);
|
|
24
|
+
readline.emitKeypressEvents(process.stdin);
|
|
25
|
+
|
|
26
|
+
if (process.stdin.isTTY) {
|
|
27
|
+
process.stdin.setRawMode(true);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
process.stdin.removeAllListeners('keypress');
|
|
31
|
+
|
|
32
|
+
process.stdin.on('keypress', (str, key) => {
|
|
33
|
+
if (key) {
|
|
34
|
+
callback(key);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
handleExit,
|
|
41
|
+
handleKeypress,
|
|
42
|
+
};
|
package/lib/projects.js
CHANGED
|
@@ -29,10 +29,6 @@ const {
|
|
|
29
29
|
fetchProject,
|
|
30
30
|
uploadProject,
|
|
31
31
|
} = require('@hubspot/cli-lib/api/dfs');
|
|
32
|
-
const {
|
|
33
|
-
logApiErrorInstance,
|
|
34
|
-
ApiErrorContext,
|
|
35
|
-
} = require('@hubspot/cli-lib/errorHandlers');
|
|
36
32
|
const { shouldIgnoreFile } = require('@hubspot/cli-lib/ignoreRules');
|
|
37
33
|
const { getCwd, getAbsoluteFilePath } = require('@hubspot/cli-lib/path');
|
|
38
34
|
const { downloadGitHubRepoContents } = require('@hubspot/cli-lib/github');
|
|
@@ -42,9 +38,11 @@ const { uiLine, uiLink, uiAccountDescription } = require('../lib/ui');
|
|
|
42
38
|
const { i18n } = require('./lang');
|
|
43
39
|
const SpinniesManager = require('./SpinniesManager');
|
|
44
40
|
const {
|
|
41
|
+
logApiErrorInstance,
|
|
42
|
+
ApiErrorContext,
|
|
45
43
|
isSpecifiedError,
|
|
46
44
|
isSpecifiedHubSpotAuthError,
|
|
47
|
-
} = require('
|
|
45
|
+
} = require('./errorHandlers/apiErrors');
|
|
48
46
|
const { HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH } = require('./constants');
|
|
49
47
|
|
|
50
48
|
const i18nKey = 'cli.lib.projects';
|
|
@@ -522,9 +520,27 @@ const handleProjectUpload = async (
|
|
|
522
520
|
|
|
523
521
|
archive.pipe(output);
|
|
524
522
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
523
|
+
let loggedIgnoredNodeModule = false;
|
|
524
|
+
|
|
525
|
+
archive.directory(srcDir, false, file => {
|
|
526
|
+
const ignored = shouldIgnoreFile(file.name, true);
|
|
527
|
+
if (ignored) {
|
|
528
|
+
const isNodeModule = file.name.includes('node_modules');
|
|
529
|
+
|
|
530
|
+
if (!isNodeModule || !loggedIgnoredNodeModule) {
|
|
531
|
+
logger.debug(
|
|
532
|
+
i18n(`${i18nKey}.handleProjectUpload.fileFiltered`, {
|
|
533
|
+
filename: file.name,
|
|
534
|
+
})
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (isNodeModule && !loggedIgnoredNodeModule) {
|
|
539
|
+
loggedIgnoredNodeModule = true;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return ignored ? false : file;
|
|
543
|
+
});
|
|
528
544
|
|
|
529
545
|
archive.finalize();
|
|
530
546
|
|