forked from FoundKeyGang/FoundKey
adapt OpenAPI documentation generation to new error definitions
This commit is contained in:
parent
1dd935dc0c
commit
fb76843c19
4 changed files with 145 additions and 170 deletions
|
@ -1,69 +0,0 @@
|
|||
|
||||
export const errors = {
|
||||
'400': {
|
||||
'INVALID_PARAM': {
|
||||
value: {
|
||||
error: {
|
||||
message: 'Invalid param.',
|
||||
code: 'INVALID_PARAM',
|
||||
id: '3d81ceae-475f-4600-b2a8-2bc116157532',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'401': {
|
||||
'CREDENTIAL_REQUIRED': {
|
||||
value: {
|
||||
error: {
|
||||
message: 'Credential required.',
|
||||
code: 'CREDENTIAL_REQUIRED',
|
||||
id: '1384574d-a912-4b81-8601-c7b1c4085df1',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'403': {
|
||||
'AUTHENTICATION_FAILED': {
|
||||
value: {
|
||||
error: {
|
||||
message: 'Authentication failed. Please ensure your token is correct.',
|
||||
code: 'AUTHENTICATION_FAILED',
|
||||
id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'418': {
|
||||
'I_AM_A_TEAPOT': {
|
||||
value: {
|
||||
error: {
|
||||
message: 'I am a teapot.',
|
||||
code: 'I_AM_A_TEAPOT',
|
||||
id: '60c46cd1-f23a-46b1-bebe-5d2b73951a84',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'429': {
|
||||
'RATE_LIMIT_EXCEEDED': {
|
||||
value: {
|
||||
error: {
|
||||
message: 'Rate limit exceeded. Please try again later.',
|
||||
code: 'RATE_LIMIT_EXCEEDED',
|
||||
id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'500': {
|
||||
'INTERNAL_ERROR': {
|
||||
value: {
|
||||
error: {
|
||||
message: 'Internal error occurred. Please contact us if the error persists.',
|
||||
code: 'INTERNAL_ERROR',
|
||||
id: '5d37dbcb-891e-41ca-a3d6-e690c97775ac',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
import config from '@/config/index.js';
|
||||
import { errors as errorDefinitions } from '../error.js';
|
||||
import endpoints from '../endpoints.js';
|
||||
import { errors as basicErrors } from './errors.js';
|
||||
import { schemas, convertSchemaToOpenApiSchema } from './schemas.js';
|
||||
import { httpCodes } from './http-codes.js';
|
||||
|
||||
export function genOpenapiSpec() {
|
||||
const spec = {
|
||||
|
@ -43,19 +44,75 @@ export function genOpenapiSpec() {
|
|||
};
|
||||
|
||||
for (const endpoint of endpoints.filter(ep => !ep.meta.secure)) {
|
||||
const errors = {} as any;
|
||||
// generate possible responses, first starting with errors
|
||||
const responses = [
|
||||
// general error codes that can always happen
|
||||
'INVALID_PARAM',
|
||||
'INTERNAL_ERROR',
|
||||
// error codes that happen only if authentication is required
|
||||
...(!endpoint.meta.requireCredential ? [] : [
|
||||
'ACCESS_DENIED',
|
||||
'AUTHENTICATION_REQUIRED',
|
||||
'AUTHENTICATION_FAILED',
|
||||
'SUSPENDED',
|
||||
]),
|
||||
// error codes that happen only if a rate limit is defined
|
||||
...(!endpoint.meta.limit ? [] : [
|
||||
'RATE_LIMIT_EXCEEDED',
|
||||
]),
|
||||
// error codes that happen only if a file is required
|
||||
...(!endpoint.meta.requireFile ? [] : [
|
||||
'FILE_REQUIRED',
|
||||
]),
|
||||
// endpoint specific error codes
|
||||
...(endpoint.meta.errors ?? []),
|
||||
]
|
||||
.reduce((acc, code) => {
|
||||
const { message, httpStatusCode } = errorDefinitions[code];
|
||||
const httpCode = httpStatusCode.toString();
|
||||
|
||||
if (endpoint.meta.errors) {
|
||||
for (const e of Object.values(endpoint.meta.errors)) {
|
||||
errors[e.code] = {
|
||||
value: {
|
||||
error: e,
|
||||
if(!(httpCode in acc)) {
|
||||
acc[httpCode] = {
|
||||
description: httpCodes[httpCode],
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
'$ref': '#/components/schemas/Error',
|
||||
},
|
||||
examples: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {};
|
||||
acc[httpCode].content['application/json'].examples[code] = {
|
||||
value: {
|
||||
error: {
|
||||
code,
|
||||
message,
|
||||
endpoint: endpoint.name,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// add successful response
|
||||
if (endpoint.meta.res) {
|
||||
responses['200'] = {
|
||||
description: 'OK',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: convertSchemaToOpenApiSchema(endpoint.meta.res),
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
responses['204'] = {
|
||||
description: 'No Content',
|
||||
};
|
||||
}
|
||||
|
||||
let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n';
|
||||
desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`;
|
||||
|
@ -107,90 +164,7 @@ export function genOpenapiSpec() {
|
|||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
...(endpoint.meta.res ? {
|
||||
'200': {
|
||||
description: 'OK (with results)',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: resSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
} : {
|
||||
'204': {
|
||||
description: 'OK (without any results)',
|
||||
},
|
||||
}),
|
||||
'400': {
|
||||
description: 'Client error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
examples: { ...errors, ...basicErrors['400'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
'401': {
|
||||
description: 'Authentication error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
examples: basicErrors['401'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'403': {
|
||||
description: 'Forbidden error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
examples: basicErrors['403'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'418': {
|
||||
description: 'I\'m Ai',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
examples: basicErrors['418'],
|
||||
},
|
||||
},
|
||||
},
|
||||
...(endpoint.meta.limit ? {
|
||||
'429': {
|
||||
description: 'Too many requests',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
examples: basicErrors['429'],
|
||||
},
|
||||
},
|
||||
},
|
||||
} : {}),
|
||||
'500': {
|
||||
description: 'Internal server error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
examples: basicErrors['500'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses,
|
||||
};
|
||||
|
||||
const path = {
|
||||
|
@ -200,6 +174,7 @@ export function genOpenapiSpec() {
|
|||
path.get = { ...info };
|
||||
// API Key authentication is not permitted for GET requests
|
||||
path.get.security = path.get.security.filter(elem => !Object.prototype.hasOwnProperty.call(elem, 'ApiKeyAuth'));
|
||||
|
||||
// fix the way parameters are passed
|
||||
delete path.get.requestBody;
|
||||
path.get.parameters = [];
|
||||
|
|
67
packages/backend/src/server/api/openapi/http-codes.ts
Normal file
67
packages/backend/src/server/api/openapi/http-codes.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
export const httpCodes: Record<string, string> = {
|
||||
"100": "Continue",
|
||||
"101": "Switching Protocols",
|
||||
"102": "Processing",
|
||||
"103": "Early Hints",
|
||||
"200": "OK",
|
||||
"201": "Created",
|
||||
"202": "Accepted",
|
||||
"203": "Non-Authoritative Information",
|
||||
"204": "No Content",
|
||||
"205": "Reset Content",
|
||||
"206": "Partial Content",
|
||||
"207": "Multi-Status",
|
||||
"208": "Already Reported",
|
||||
"226": "IM Used",
|
||||
"300": "Multiple Choices",
|
||||
"301": "Moved Permanently",
|
||||
"302": "Found",
|
||||
"303": "See Other",
|
||||
"304": "Not Modified",
|
||||
"305": "Use Proxy",
|
||||
"307": "Temporary Redirect",
|
||||
"308": "Permanent Redirect",
|
||||
"400": "Bad Request",
|
||||
"401": "Unauthorized",
|
||||
"402": "Payment Required",
|
||||
"403": "Forbidden",
|
||||
"404": "Not Found",
|
||||
"405": "Method Not Allowed",
|
||||
"406": "Not Acceptable",
|
||||
"407": "Proxy Authentication Required",
|
||||
"408": "Request Timeout",
|
||||
"409": "Conflict",
|
||||
"410": "Gone",
|
||||
"411": "Length Required",
|
||||
"412": "Precondition Failed",
|
||||
"413": "Content Too Large",
|
||||
"414": "URI Too Long",
|
||||
"415": "Unsupported Media Type",
|
||||
"416": "Range Not Satisfiable",
|
||||
"417": "Expectation Failed",
|
||||
"418": "I'm a Teapot",
|
||||
"421": "Misdirected Request",
|
||||
"422": "Unprocessable Content",
|
||||
"423": "Locked",
|
||||
"424": "Failed Dependency",
|
||||
"425": "Too Early",
|
||||
"426": "Upgrade Required",
|
||||
"427": "Unassigned",
|
||||
"428": "Precondition Required",
|
||||
"429": "Too Many Requests",
|
||||
"430": "Unassigned",
|
||||
"431": "Request Header Fields Too Large",
|
||||
"451": "Unavailable For Legal Reasons",
|
||||
"500": "Internal Server Error",
|
||||
"501": "Not Implemented",
|
||||
"502": "Bad Gateway",
|
||||
"503": "Service Unavailable",
|
||||
"504": "Gateway Timeout",
|
||||
"505": "HTTP Version Not Supported",
|
||||
"506": "Variant Also Negotiates",
|
||||
"507": "Insufficient Storage",
|
||||
"508": "Loop Detected",
|
||||
"509": "Unassigned",
|
||||
"510": "Not Extended",
|
||||
"511": "Network Authentication Required"
|
||||
}
|
|
@ -36,19 +36,21 @@ export const schemas = {
|
|||
properties: {
|
||||
code: {
|
||||
type: 'string',
|
||||
description: 'An error code. Unique within the endpoint.',
|
||||
description: 'A machine and human readable error code.',
|
||||
},
|
||||
endpoint: {
|
||||
type: 'string',
|
||||
description: 'Name of the API endpoint the error happened in.',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'An error message.',
|
||||
},
|
||||
id: {
|
||||
type: 'string',
|
||||
format: 'uuid',
|
||||
description: 'An error ID. This ID is static.',
|
||||
description: 'A human readable error description in English.',
|
||||
},
|
||||
info: {
|
||||
description: 'Potentially more information, primarily intended for developers.',
|
||||
}
|
||||
},
|
||||
required: ['code', 'id', 'message'],
|
||||
required: ['code', 'endpoint', 'message'],
|
||||
},
|
||||
},
|
||||
required: ['error'],
|
||||
|
|
Loading…
Reference in a new issue