This repository has been archived by the owner on Oct 25, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Better error management, fix Contentful import
- Loading branch information
1 parent
810504d
commit 8df807c
Showing
24 changed files
with
642 additions
and
611 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,46 @@ | ||
#!/usr/bin/env node | ||
#!/usr/bin/env node --async-stack-traces | ||
|
||
const PrettyError = require('pretty-error'); | ||
const colors = require('colors'); | ||
|
||
require('../lib'); | ||
const runCli = require('../lib/cli'); | ||
const ApiException = require('../lib/ApiException').default; | ||
|
||
runCli().catch(e => { | ||
process.stdout.write(`Command failed with the following error:\n`); | ||
process.stdout.write(`${e.message}\n`); | ||
process.stderr.write(colors.brightRed(`\nCommand failed!\n`)); | ||
|
||
if (e instanceof ApiException) { | ||
const humanMessage = e.humanMessageForFailedResponse(); | ||
|
||
if (humanMessage) { | ||
process.stderr.write(`${colors.red.underline(humanMessage)} \n\n`); | ||
} | ||
|
||
process.stderr.write(colors.underline.gray(`\nFailed request:\n\n`)); | ||
|
||
process.stderr.write(`${e.requestMethod} ${e.requestUrl}\n\n`); | ||
for (const [key, value] of Object.entries(e.requestHeaders)) { | ||
process.stderr.write(`${key}: ${value}\n`); | ||
} | ||
if (e.requestBody) { | ||
process.stderr.write(`\n${e.requestBody}`); | ||
} | ||
|
||
process.stderr.write(colors.underline.gray(`\n\nHTTP Response:\n\n`)); | ||
|
||
process.stderr.write(`${e.statusCode} ${e.statusText}\n\n`); | ||
for (const [key, value] of Object.entries(e.headers)) { | ||
process.stderr.write(`${key}: ${value}\n`); | ||
} | ||
|
||
if (e.body) { | ||
process.stderr.write(`\n${JSON.stringify(e.body)}`); | ||
} | ||
} | ||
|
||
process.stderr.write(colors.underline.gray(`\n\nException details:\n\n`)); | ||
process.stderr.write(new PrettyError().render(e)); | ||
|
||
process.exit(1); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,99 @@ | ||
export default function ApiException(response, body) { | ||
export default function ApiException( | ||
response, | ||
body, | ||
{ url, options, preCallStack }, | ||
) { | ||
if ('captureStackTrace' in Error) { | ||
Error.captureStackTrace(this, ApiException); | ||
} else { | ||
this.stack = new Error().stack; | ||
} | ||
|
||
if (response) { | ||
if (response.status < 500) { | ||
const error = body.data[0]; | ||
const details = JSON.stringify(error.attributes.details); | ||
this.message = `${response.status} ${error.attributes.code} (details: ${details})`; | ||
} else { | ||
this.message = `${response.status} ${response.statusText}`; | ||
} | ||
|
||
this.body = body; | ||
this.headers = response.headers; | ||
this.statusCode = response.status; | ||
this.statusText = response.statusText; | ||
if (response.status < 500) { | ||
const error = body.data[0]; | ||
const details = JSON.stringify(error.attributes.details); | ||
this.message = `${response.status} ${error.attributes.code} (details: ${details})`; | ||
} else { | ||
this.message = 'Misconfigured exception'; | ||
this.message = `${response.status} ${response.statusText}`; | ||
} | ||
|
||
this.body = body; | ||
this.headers = response.headers.raw(); | ||
this.statusCode = response.status; | ||
this.statusText = response.statusText; | ||
this.requestUrl = url; | ||
this.requestMethod = options.method || 'GET'; | ||
this.requestHeaders = options.headers; | ||
this.requestBody = options.body; | ||
this.stack += `\nCaused By:\n${preCallStack}`; | ||
} | ||
|
||
ApiException.prototype = Object.create(Error.prototype); | ||
ApiException.prototype.name = 'ApiException'; | ||
ApiException.prototype.constructor = ApiException; | ||
ApiException.prototype.errorWithCode = function errorWithCode(code) { | ||
|
||
ApiException.prototype.errorWithCode = function errorWithCode(codeOrCodes) { | ||
const codes = Array.isArray(codeOrCodes) ? codeOrCodes : [codeOrCodes]; | ||
|
||
if (!this.body || !(this.body.data instanceof Array)) { | ||
return null; | ||
} | ||
|
||
return this.body.data.find(error => error.attributes.code === code); | ||
return this.body.data.find(error => codes.includes(error.attributes.code)); | ||
}; | ||
|
||
const humanMessageForCode = { | ||
BATCH_DATA_VALIDATION_IN_PROGRESS: `The schema of this model changed, we're re-running validations over every record in background. Please retry with this operation in a few seconds!`, | ||
INSUFFICIENT_PERMISSIONS: `Your role does not permit this action`, | ||
MAINTENANCE_MODE: `The project is currently in maintenance mode!`, | ||
DELETE_RESTRICTION: `Sorry, but you cannot delete this resource, as it's currently used/referenced elsewhere!`, | ||
INVALID_CREDENTIALS: `Credentials are incorrect!`, | ||
INVALID_EMAIL: `Email address is incorrect!`, | ||
INVALID_FORMAT: `The format of the parameters passed is incorrect, take a look at the details of the error to know what's wrong!`, | ||
ITEM_LOCKED: `The operation cannot be completed as some other user is currently editing this record!`, | ||
LINKED_FROM_PUBLISHED_ITEMS: `Couldn't unpublish the record, as some published records are linked to it!`, | ||
PLAN_UPGRADE_REQUIRED: `Cannot proceed, please upgrade plan!`, | ||
PUBLISHED_CHILDREN: `Couldn't unpublish the record, some children records are still published!`, | ||
REQUIRED_2FA_SETUP: `This project requires every user to turn on 2-factor authentication! Please go to your Dashboard and activate it! (https://dashboard.datocms.com/account/setup-2fa)`, | ||
REQUIRED_BY_ASSOCIATION: `Cannot delete the record, as it's required by other records:`, | ||
STALE_ITEM_VERSION: `Someone else made a change while you were editing this record, please refresh the page!`, | ||
TITLE_ALREADY_PRESENT: `There can only be one Title field per model`, | ||
UNPUBLISHED_LINK: `Couldn't publish the record, as it links some unpublished records!`, | ||
UNPUBLISHED_PARENT: `Couldn't publish the record, as the parent record is not published!`, | ||
UPLOAD_IS_CURRENTLY_IN_USE: `Couldn't delete this asset, as it's currently used by some records!`, | ||
UPLOAD_NOT_PASSING_FIELD_VALIDATIONS: `Couldn't update this asset since some records are failing to pass the validations!`, | ||
}; | ||
|
||
const humanMessageForPlanUpgradeLimit = { | ||
build_triggers: `You've reached the maximum number of build triggers your plan allows`, | ||
sandbox_environments: `You've reached the maximum number of environments your plan allows`, | ||
item_types: `You've reached the maximum number of models your plan allows to create`, | ||
items: `You've reached the maximum number of records your plan allows to create`, | ||
locales: `You've reached the maximum number of locales your plan allows`, | ||
mux_encoding_seconds: `You've reached the maximum video encoding limits of your plan`, | ||
otp: `Two-factor authentication cannot be on the current plan`, | ||
plugins: `You've reached the maximum number of plugins your plan allows`, | ||
roles: `You've reached the maximum number of roles your plan allows to create`, | ||
uploadable_bytes: `You've reached the file storage limits of your plan`, | ||
users: `You've reached the maximum number of collaborators your plan allows to invite to the project`, | ||
access_tokens: `You've reached the maximum number of API tokens your plan allows to create`, | ||
}; | ||
|
||
ApiException.prototype.humanMessageForFailedResponse = function humanMessageForFailedResponse() { | ||
const planUpgradeError = this.errorWithCode('PLAN_UPGRADE_REQUIRED'); | ||
|
||
if (planUpgradeError) { | ||
const { limit } = planUpgradeError.attributes.details; | ||
return `${humanMessageForPlanUpgradeLimit[limit]}. Please head over to your account dashboard (https://dashboard.datocms.com/) to upgrade the plan or, if no publicly available plan suits your needs, contact our Sales team (https://www.datocms.com/contact) to get a custom quote!`; | ||
} | ||
|
||
const errors = Object.keys(humanMessageForCode) | ||
.filter(code => this.errorWithCode(code)) | ||
.map(code => humanMessageForCode[code]); | ||
|
||
if (errors.length === 0) { | ||
return null; | ||
} | ||
|
||
return errors.join('\n'); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.