From b70628264ea29565270275952fcc47c71a7314be Mon Sep 17 00:00:00 2001 From: didier amyot Date: Tue, 13 Jun 2023 11:51:42 -0400 Subject: [PATCH] Added better http error code in rest api --- src/deps.ts | 5 + src/restapi.ts | 24 +- .../deno.land/std@0.191.0/http/http_status.ts | 343 ++++++++++++++++++ 3 files changed, 366 insertions(+), 6 deletions(-) create mode 100644 vendor/deno.land/std@0.191.0/http/http_status.ts diff --git a/src/deps.ts b/src/deps.ts index 157ca52..788556c 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -1,3 +1,5 @@ +// deno std + export { Buffer, StringReader } from 'https://deno.land/std@0.191.0/io/mod.ts'; export { iterateReader, @@ -7,4 +9,7 @@ export { readerFromStreamReader, TextLineStream, } from 'https://deno.land/std@0.191.0/streams/mod.ts'; +export {Status} from "https://deno.land/std@0.191.0/http/http_status.ts"; + +// oak export { Application, Context, Router } from 'https://deno.land/x/oak/mod.ts'; diff --git a/src/restapi.ts b/src/restapi.ts index 27ddb13..40b5b58 100644 --- a/src/restapi.ts +++ b/src/restapi.ts @@ -1,9 +1,7 @@ -import { Context, Router, TextLineStream, readableStreamFromReader } from './deps.ts'; +import { Context, Router, Status} from './deps.ts'; import { FileMetaData, FileService } from './services/mod.ts'; import { readerFromStreamReader } from './deps.ts'; import { asyncIterableToArray } from './utils.ts'; -import { Status } from "../vendor/deno.land/std@0.178.0/http/http_status.ts"; -import { WaveFrontServiceImpl } from "./services/wavefrontService.ts"; import { Vector3D, WaveFrontService } from './services/interfaces.ts'; type Handler = (ctx: Context) => void; @@ -41,6 +39,12 @@ function createFileHandlerFactory(fileService: FileService): Handler { const path = untypedCtx.request.url.searchParams.get('path'); if(!path){ ctx.response.status = Status.BadRequest; + ctx.response.body = `Requires a "path" url parameter.` + return; + } + if(!ctx.request.hasBody){ + ctx.response.status = Status.BadRequest; + ctx.response.body = `Requires a body to the POST request (the file basically).` return; } const reqBody = await ctx.request.body({ type: 'stream' }).value; @@ -57,11 +61,14 @@ function renameFileHandlerFactory(fileService: FileService): Handler { const reqBody = await ctx.request.body().value; const newPath = reqBody.path; if(!reqBody.path){ + // nothing to do. + ctx.response.status = Status.OK; return; } const ret = await fileService.renameById(fileId, newPath); if(ret === null){ ctx.response.status = Status.NotFound; + ctx.response.body = `No File with id "${fileId}".` return } @@ -77,6 +84,7 @@ function getFileHandlerFactory(fileService: FileService): Handler { const fm = await fileService.getMetaDataByID(fileId); if (fm == null) { ctx.response.status = 404; + ctx.response.body = `No File with id "${fileId}".` return; } ctx.response.status = 200; @@ -91,6 +99,7 @@ function getDownloadFileHandlerFactory(fileService: FileService): Handler { const reader = await fileService.getContentById(fileId); if (reader == null) { ctx.response.status = 404; + ctx.response.body = `No File with id "${fileId}".` return; } ctx.response.status = 200; @@ -120,7 +129,8 @@ function getDownloadTransformedFileHandlerFactory(fileService: FileService, wave const fileId = untypedCtx.params.fileId; const reader = await fileService.getContentById(fileId); if (reader == null) { - ctx.response.status = 404; + ctx.response.status = Status.NotFound; + ctx.response.body = `No File with id "${fileId}".` return; } @@ -128,6 +138,7 @@ function getDownloadTransformedFileHandlerFactory(fileService: FileService, wave const translate = getTranslateVector(untypedCtx.request.url.searchParams); if(scale === null || translate === null){ ctx.response.status = Status.BadRequest; + ctx.response.body = `Bad scale of translate parameters.` return; } ctx.response.status = 200; @@ -138,10 +149,11 @@ function getDownloadTransformedFileHandlerFactory(fileService: FileService, wave function deleteFileHandlerFactory(fileService: FileService): Handler { return async (ctx: Context) => { const untypedCtx: any = ctx; - const path = untypedCtx.params.fileId; - const isDeleted = await fileService.deleteById(path); + const fileId = untypedCtx.params.fileId; + const isDeleted = await fileService.deleteById(fileId); if (!isDeleted) { ctx.response.status = 404; + ctx.response.body = `No File with id "${fileId}".` return; } ctx.response.status = 200; diff --git a/vendor/deno.land/std@0.191.0/http/http_status.ts b/vendor/deno.land/std@0.191.0/http/http_status.ts new file mode 100644 index 0000000..12c467c --- /dev/null +++ b/vendor/deno.land/std@0.191.0/http/http_status.ts @@ -0,0 +1,343 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// This module is browser compatible. + +/** + * Contains the enum {@linkcode Status} which enumerates standard HTTP status + * codes and provides several type guards for handling status codes with type + * safety. + * + * @example + * ```ts + * import { + * Status, + * STATUS_TEXT, + * } from "https://deno.land/std@$STD_VERSION/http/http_status.ts"; + * + * console.log(Status.NotFound); //=> 404 + * console.log(STATUS_TEXT[Status.NotFound]); //=> "Not Found" + * ``` + * + * ```ts + * import { isErrorStatus } from "https://deno.land/std@$STD_VERSION/http/http_status.ts"; + * + * const res = await fetch("https://example.com/"); + * + * if (isErrorStatus(res.status)) { + * // error handling here... + * } + * ``` + * + * @module + */ + +/** Standard HTTP status codes. */ +export enum Status { + /** RFC 7231, 6.2.1 */ + Continue = 100, + /** RFC 7231, 6.2.2 */ + SwitchingProtocols = 101, + /** RFC 2518, 10.1 */ + Processing = 102, + /** RFC 8297 **/ + EarlyHints = 103, + + /** RFC 7231, 6.3.1 */ + OK = 200, + /** RFC 7231, 6.3.2 */ + Created = 201, + /** RFC 7231, 6.3.3 */ + Accepted = 202, + /** RFC 7231, 6.3.4 */ + NonAuthoritativeInfo = 203, + /** RFC 7231, 6.3.5 */ + NoContent = 204, + /** RFC 7231, 6.3.6 */ + ResetContent = 205, + /** RFC 7233, 4.1 */ + PartialContent = 206, + /** RFC 4918, 11.1 */ + MultiStatus = 207, + /** RFC 5842, 7.1 */ + AlreadyReported = 208, + /** RFC 3229, 10.4.1 */ + IMUsed = 226, + + /** RFC 7231, 6.4.1 */ + MultipleChoices = 300, + /** RFC 7231, 6.4.2 */ + MovedPermanently = 301, + /** RFC 7231, 6.4.3 */ + Found = 302, + /** RFC 7231, 6.4.4 */ + SeeOther = 303, + /** RFC 7232, 4.1 */ + NotModified = 304, + /** RFC 7231, 6.4.5 */ + UseProxy = 305, + /** RFC 7231, 6.4.7 */ + TemporaryRedirect = 307, + /** RFC 7538, 3 */ + PermanentRedirect = 308, + + /** RFC 7231, 6.5.1 */ + BadRequest = 400, + /** RFC 7235, 3.1 */ + Unauthorized = 401, + /** RFC 7231, 6.5.2 */ + PaymentRequired = 402, + /** RFC 7231, 6.5.3 */ + Forbidden = 403, + /** RFC 7231, 6.5.4 */ + NotFound = 404, + /** RFC 7231, 6.5.5 */ + MethodNotAllowed = 405, + /** RFC 7231, 6.5.6 */ + NotAcceptable = 406, + /** RFC 7235, 3.2 */ + ProxyAuthRequired = 407, + /** RFC 7231, 6.5.7 */ + RequestTimeout = 408, + /** RFC 7231, 6.5.8 */ + Conflict = 409, + /** RFC 7231, 6.5.9 */ + Gone = 410, + /** RFC 7231, 6.5.10 */ + LengthRequired = 411, + /** RFC 7232, 4.2 */ + PreconditionFailed = 412, + /** RFC 7231, 6.5.11 */ + RequestEntityTooLarge = 413, + /** RFC 7231, 6.5.12 */ + RequestURITooLong = 414, + /** RFC 7231, 6.5.13 */ + UnsupportedMediaType = 415, + /** RFC 7233, 4.4 */ + RequestedRangeNotSatisfiable = 416, + /** RFC 7231, 6.5.14 */ + ExpectationFailed = 417, + /** RFC 7168, 2.3.3 */ + Teapot = 418, + /** RFC 7540, 9.1.2 */ + MisdirectedRequest = 421, + /** RFC 4918, 11.2 */ + UnprocessableEntity = 422, + /** RFC 4918, 11.3 */ + Locked = 423, + /** RFC 4918, 11.4 */ + FailedDependency = 424, + /** RFC 8470, 5.2 */ + TooEarly = 425, + /** RFC 7231, 6.5.15 */ + UpgradeRequired = 426, + /** RFC 6585, 3 */ + PreconditionRequired = 428, + /** RFC 6585, 4 */ + TooManyRequests = 429, + /** RFC 6585, 5 */ + RequestHeaderFieldsTooLarge = 431, + /** RFC 7725, 3 */ + UnavailableForLegalReasons = 451, + + /** RFC 7231, 6.6.1 */ + InternalServerError = 500, + /** RFC 7231, 6.6.2 */ + NotImplemented = 501, + /** RFC 7231, 6.6.3 */ + BadGateway = 502, + /** RFC 7231, 6.6.4 */ + ServiceUnavailable = 503, + /** RFC 7231, 6.6.5 */ + GatewayTimeout = 504, + /** RFC 7231, 6.6.6 */ + HTTPVersionNotSupported = 505, + /** RFC 2295, 8.1 */ + VariantAlsoNegotiates = 506, + /** RFC 4918, 11.5 */ + InsufficientStorage = 507, + /** RFC 5842, 7.2 */ + LoopDetected = 508, + /** RFC 2774, 7 */ + NotExtended = 510, + /** RFC 6585, 6 */ + NetworkAuthenticationRequired = 511, +} + +/** A record of all the status codes text. */ +export const STATUS_TEXT: Readonly> = { + [Status.Accepted]: "Accepted", + [Status.AlreadyReported]: "Already Reported", + [Status.BadGateway]: "Bad Gateway", + [Status.BadRequest]: "Bad Request", + [Status.Conflict]: "Conflict", + [Status.Continue]: "Continue", + [Status.Created]: "Created", + [Status.EarlyHints]: "Early Hints", + [Status.ExpectationFailed]: "Expectation Failed", + [Status.FailedDependency]: "Failed Dependency", + [Status.Forbidden]: "Forbidden", + [Status.Found]: "Found", + [Status.GatewayTimeout]: "Gateway Timeout", + [Status.Gone]: "Gone", + [Status.HTTPVersionNotSupported]: "HTTP Version Not Supported", + [Status.IMUsed]: "IM Used", + [Status.InsufficientStorage]: "Insufficient Storage", + [Status.InternalServerError]: "Internal Server Error", + [Status.LengthRequired]: "Length Required", + [Status.Locked]: "Locked", + [Status.LoopDetected]: "Loop Detected", + [Status.MethodNotAllowed]: "Method Not Allowed", + [Status.MisdirectedRequest]: "Misdirected Request", + [Status.MovedPermanently]: "Moved Permanently", + [Status.MultiStatus]: "Multi Status", + [Status.MultipleChoices]: "Multiple Choices", + [Status.NetworkAuthenticationRequired]: "Network Authentication Required", + [Status.NoContent]: "No Content", + [Status.NonAuthoritativeInfo]: "Non Authoritative Info", + [Status.NotAcceptable]: "Not Acceptable", + [Status.NotExtended]: "Not Extended", + [Status.NotFound]: "Not Found", + [Status.NotImplemented]: "Not Implemented", + [Status.NotModified]: "Not Modified", + [Status.OK]: "OK", + [Status.PartialContent]: "Partial Content", + [Status.PaymentRequired]: "Payment Required", + [Status.PermanentRedirect]: "Permanent Redirect", + [Status.PreconditionFailed]: "Precondition Failed", + [Status.PreconditionRequired]: "Precondition Required", + [Status.Processing]: "Processing", + [Status.ProxyAuthRequired]: "Proxy Auth Required", + [Status.RequestEntityTooLarge]: "Request Entity Too Large", + [Status.RequestHeaderFieldsTooLarge]: "Request Header Fields Too Large", + [Status.RequestTimeout]: "Request Timeout", + [Status.RequestURITooLong]: "Request URI Too Long", + [Status.RequestedRangeNotSatisfiable]: "Requested Range Not Satisfiable", + [Status.ResetContent]: "Reset Content", + [Status.SeeOther]: "See Other", + [Status.ServiceUnavailable]: "Service Unavailable", + [Status.SwitchingProtocols]: "Switching Protocols", + [Status.Teapot]: "I'm a teapot", + [Status.TemporaryRedirect]: "Temporary Redirect", + [Status.TooEarly]: "Too Early", + [Status.TooManyRequests]: "Too Many Requests", + [Status.Unauthorized]: "Unauthorized", + [Status.UnavailableForLegalReasons]: "Unavailable For Legal Reasons", + [Status.UnprocessableEntity]: "Unprocessable Entity", + [Status.UnsupportedMediaType]: "Unsupported Media Type", + [Status.UpgradeRequired]: "Upgrade Required", + [Status.UseProxy]: "Use Proxy", + [Status.VariantAlsoNegotiates]: "Variant Also Negotiates", +}; + +/** An HTTP status that is a informational (1XX). */ +export type InformationalStatus = + | Status.Continue + | Status.SwitchingProtocols + | Status.Processing + | Status.EarlyHints; + +/** An HTTP status that is a success (2XX). */ +export type SuccessfulStatus = + | Status.OK + | Status.Created + | Status.Accepted + | Status.NonAuthoritativeInfo + | Status.NoContent + | Status.ResetContent + | Status.PartialContent + | Status.MultiStatus + | Status.AlreadyReported + | Status.IMUsed; + +/** An HTTP status that is a redirect (3XX). */ +export type RedirectStatus = + | Status.MultipleChoices // 300 + | Status.MovedPermanently // 301 + | Status.Found // 302 + | Status.SeeOther // 303 + | Status.UseProxy // 305 - DEPRECATED + | Status.TemporaryRedirect // 307 + | Status.PermanentRedirect; // 308 + +/** An HTTP status that is a client error (4XX). */ +export type ClientErrorStatus = + | Status.BadRequest + | Status.Unauthorized + | Status.PaymentRequired + | Status.Forbidden + | Status.NotFound + | Status.MethodNotAllowed + | Status.NotAcceptable + | Status.ProxyAuthRequired + | Status.RequestTimeout + | Status.Conflict + | Status.Gone + | Status.LengthRequired + | Status.PreconditionFailed + | Status.RequestEntityTooLarge + | Status.RequestURITooLong + | Status.UnsupportedMediaType + | Status.RequestedRangeNotSatisfiable + | Status.ExpectationFailed + | Status.Teapot + | Status.MisdirectedRequest + | Status.UnprocessableEntity + | Status.Locked + | Status.FailedDependency + | Status.UpgradeRequired + | Status.PreconditionRequired + | Status.TooManyRequests + | Status.RequestHeaderFieldsTooLarge + | Status.UnavailableForLegalReasons; + +/** An HTTP status that is a server error (5XX). */ +export type ServerErrorStatus = + | Status.InternalServerError + | Status.NotImplemented + | Status.BadGateway + | Status.ServiceUnavailable + | Status.GatewayTimeout + | Status.HTTPVersionNotSupported + | Status.VariantAlsoNegotiates + | Status.InsufficientStorage + | Status.LoopDetected + | Status.NotExtended + | Status.NetworkAuthenticationRequired; + +/** An HTTP status that is an error (4XX and 5XX). */ +export type ErrorStatus = ClientErrorStatus | ServerErrorStatus; + +/** A type guard that determines if the status code is informational. */ +export function isInformationalStatus( + status: Status, +): status is InformationalStatus { + return status >= 100 && status < 200; +} + +/** A type guard that determines if the status code is successful. */ +export function isSuccessfulStatus(status: Status): status is SuccessfulStatus { + return status >= 200 && status < 300; +} + +/** A type guard that determines if the status code is a redirection. */ +export function isRedirectStatus(status: Status): status is RedirectStatus { + return status >= 300 && status < 400; +} + +/** A type guard that determines if the status code is a client error. */ +export function isClientErrorStatus( + status: Status, +): status is ClientErrorStatus { + return status >= 400 && status < 500; +} + +/** A type guard that determines if the status code is a server error. */ +export function isServerErrorStatus( + status: Status, +): status is ServerErrorStatus { + return status >= 500 && status < 600; +} + +/** A type guard that determines if the status code is an error. */ +export function isErrorStatus(status: Status): status is ErrorStatus { + return status >= 400 && status < 600; +}