apify-client
Version:
Apify API client for JavaScript
199 lines • 6.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApifyApiError = void 0;
const body_parser_1 = require("./body_parser");
const utils_1 = require("./utils");
/**
* Examples of capturing groups for "...at ActorCollectionClient._list (/Users/..."
* 0: "at ActorCollectionClient._list ("
* 1: undefined
* 2: "ActorCollectionClient"
* 3: undefined
* 4: "list"
* @private
*/
const CLIENT_METHOD_REGEX = /at( async)? ([A-Za-z]+(Collection)?Client)\._?([A-Za-z]+) \(/;
/**
* An `ApifyApiError` is thrown for successful HTTP requests that reach the API,
* but the API responds with an error response. Typically, those are rate limit
* errors and internal errors, which are automatically retried, or validation
* errors, which are thrown immediately, because a correction by the user is
* needed.
*/
class ApifyApiError extends Error {
/**
* @hidden
*/
constructor(response, attempt) {
var _a;
let message;
let type;
let responseData = response.data;
let errorData;
// Some methods (e.g. downloadItems) set up forceBuffer on request response. If this request failed
// the body buffer needs to parse to get the correct error.
if ((0, utils_1.isBuffer)(responseData)) {
try {
responseData = JSON.parse((0, body_parser_1.isomorphicBufferToString)(response.data, 'utf-8'));
}
catch {
// This can happen. The data in the response body are malformed.
}
}
if (responseData && responseData.error) {
const { error } = responseData;
message = error.message;
type = error.type;
errorData = error.data;
}
else if (responseData) {
let dataString;
try {
dataString = JSON.stringify(responseData, null, 2);
}
catch {
dataString = `${responseData}`;
}
message = `Unexpected error: ${dataString}`;
}
super(message);
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* The invoked resource client and the method. Known issue: Sometimes it displays
* as `unknown` because it can't be parsed from a stack trace.
*/
Object.defineProperty(this, "clientMethod", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* HTTP status code of the error.
*/
Object.defineProperty(this, "statusCode", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* The type of the error, as returned by the API.
*/
Object.defineProperty(this, "type", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Number of the API call attempt.
*/
Object.defineProperty(this, "attempt", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* HTTP method of the API call.
*/
Object.defineProperty(this, "httpMethod", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Full path of the API endpoint (URL excluding origin).
*/
Object.defineProperty(this, "path", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Original stack trace of the exception. It is replaced
* by a more informative stack with API call information.
*/
Object.defineProperty(this, "originalStack", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Additional data provided by the API about the error
*/
Object.defineProperty(this, "data", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.name = this.constructor.name;
this.clientMethod = this._extractClientAndMethodFromStack();
this.statusCode = response.status;
this.type = type;
this.attempt = attempt;
this.httpMethod = (_a = response.config) === null || _a === void 0 ? void 0 : _a.method;
this.path = this._safelyParsePathFromResponse(response);
const stack = this.stack;
this.originalStack = stack.slice(stack.indexOf('\n'));
this.stack = this._createApiStack();
this.data = errorData;
}
_safelyParsePathFromResponse(response) {
var _a;
const urlString = (_a = response.config) === null || _a === void 0 ? void 0 : _a.url;
let url;
try {
url = new URL(urlString);
}
catch {
return urlString;
}
return url.pathname + url.search;
}
_extractClientAndMethodFromStack() {
const match = this.stack.match(CLIENT_METHOD_REGEX);
if (match)
return `${match[2]}.${match[4]}`;
return 'unknown';
}
/**
* Creates a better looking and more informative stack that will be printed
* out when API errors are thrown.
*
* Example:
*
* ApifyApiError: Actor task was not found
* clientMethod: TaskClient.start
* statusCode: 404
* type: record-not-found
* attempt: 1
* httpMethod: post
* path: /v2/actor-tasks/user~my-task/runs
*/
_createApiStack() {
const { name, ...props } = this;
const stack = Object.entries(props)
.map(([k, v]) => {
// Rename originalStack to stack in the stack itself.
// This is for better readability of errors in log.
if (k === 'originalStack')
k = 'stack';
return ` ${k}: ${v}`;
})
.join('\n');
return `${name}: ${this.message}\n${stack}`;
}
}
exports.ApifyApiError = ApifyApiError;
//# sourceMappingURL=apify_api_error.js.map