conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

errors.js (4674B)


      1 // src/lib/errors.js - errors that describe themselves over HTTP
      2 //
      3 // The class name is the identifier on the wire, and code is the HTTP status:
      4 // serialize-error-cjs carries both, where a property of our own would be lost.
      5 // Statics rather than fields, since a subclass field would clobber an override.
      6 
      7 export class ApiError extends Error {
      8   static code = 500;
      9   static defaultMessage = 'The request could not be completed';
     10 
     11   constructor(message, { code, details } = {}) {
     12     super(message ?? new.target.defaultMessage);
     13     this.name = new.target.name;
     14     this.code = code ?? new.target.code;
     15     if (details !== undefined) this.details = details;
     16   }
     17 }
     18 
     19 // Carries details.request_id, tying a report of "it broke" to the log line.
     20 export class InternalError extends ApiError {
     21   static code = 500;
     22 }
     23 
     24 // For a 4xx nobody threw deliberately, such as a body Fastify itself refused.
     25 export class BadRequest extends ApiError {
     26   static code = 400;
     27   static defaultMessage = 'The request could not be understood';
     28 }
     29 
     30 // --- not there, or not yours: both answer 404, so neither can be probed ---
     31 
     32 export class UnknownProject extends ApiError {
     33   static code = 404;
     34   static defaultMessage = 'No such project';
     35 }
     36 
     37 export class UnknownJob extends ApiError {
     38   static code = 404;
     39   static defaultMessage = 'No such job';
     40 }
     41 
     42 export class UnknownTask extends ApiError {
     43   static code = 404;
     44   static defaultMessage = 'No such task';
     45 }
     46 
     47 export class UnknownArtifact extends ApiError {
     48   static code = 404;
     49   static defaultMessage = 'No such artifact';
     50 }
     51 
     52 export class ArtifactNotStored extends ApiError {
     53   static code = 404;
     54   static defaultMessage = 'That artifact is no longer stored';
     55 }
     56 
     57 export class NotFound extends ApiError {
     58   static code = 404;
     59   static defaultMessage = 'No such endpoint';
     60 }
     61 
     62 // --- refused ---
     63 
     64 export class InvalidSignature extends ApiError {
     65   static code = 401;
     66   static defaultMessage = 'The request signature is missing or wrong';
     67 }
     68 
     69 export class InvalidWorkerToken extends ApiError {
     70   static code = 401;
     71   static defaultMessage = 'The worker token is missing or wrong';
     72 }
     73 
     74 export class NotAuthenticated extends ApiError {
     75   static code = 401;
     76   static defaultMessage = 'This endpoint needs an access token';
     77 }
     78 
     79 // Only for something the caller is allowed to know exists. Anything else is
     80 // a 404, so identifiers cannot be probed.
     81 export class NotPermitted extends ApiError {
     82   static code = 403;
     83   static defaultMessage = 'That is not yours to do';
     84 }
     85 
     86 export class Conflict extends ApiError {
     87   static code = 409;
     88   static defaultMessage = 'That conflicts with something that already exists';
     89 }
     90 
     91 // 403 reaching for a task it never held, 409 losing one mid-report.
     92 export class TaskNotHeld extends ApiError {
     93   static code = 403;
     94   static defaultMessage = 'That task is not held by this worker';
     95 }
     96 
     97 // --- not actionable as it stands ---
     98 
     99 export class ProjectDisabled extends ApiError {
    100   static code = 409;
    101   static defaultMessage = 'That project is disabled';
    102 }
    103 
    104 export class TaskNotRunning extends ApiError {
    105   static code = 409;
    106   static defaultMessage = 'That task is not running';
    107 }
    108 
    109 // Carries details.expected_offset, which is what makes a resume safe.
    110 export class LogOffsetConflict extends ApiError {
    111   static code = 409;
    112   static defaultMessage = 'The log chunk does not continue from the end of the log';
    113 }
    114 
    115 // --- malformed ---
    116 
    117 export class MalformedJson extends ApiError {
    118   static code = 400;
    119   static defaultMessage = 'The request body is not valid JSON';
    120 }
    121 
    122 export class NoCommitInPayload extends ApiError {
    123   static code = 400;
    124   static defaultMessage =
    125     'Could not read a commit from the payload; expected sha, or a GitHub, Gitea or GitLab push event';
    126 }
    127 
    128 export class InvalidSha extends ApiError {
    129   static code = 400;
    130   static defaultMessage = 'That is not a full commit id';
    131 }
    132 
    133 export class ShaRequired extends ApiError {
    134   static code = 400;
    135   static defaultMessage = 'sha is required';
    136 }
    137 
    138 export class InvalidLogOffset extends ApiError {
    139   static code = 400;
    140   static defaultMessage = 'x-log-offset must be an integer';
    141 }
    142 
    143 export class InvalidArtifactPath extends ApiError {
    144   static code = 400;
    145   static defaultMessage = 'x-artifact-path is missing or unusable';
    146 }
    147 
    148 export class ArtifactSizeMismatch extends ApiError {
    149   static code = 400;
    150   static defaultMessage = 'The artifact body does not match its declared length';
    151 }
    152 
    153 export class ContentLengthRequired extends ApiError {
    154   static code = 411;
    155   static defaultMessage = 'content-length is required for artifact uploads';
    156 }
    157 
    158 export class LogChunkTooLarge extends ApiError {
    159   static code = 413;
    160   static defaultMessage = 'That log chunk is too large';
    161 }