wip
This commit is contained in:
@@ -3,7 +3,7 @@ import Ajv from 'ajv';
|
||||
import type { LocalUser } from '@/models/entities/User.js';
|
||||
import type { AccessToken } from '@/models/entities/AccessToken.js';
|
||||
import { ApiError } from './error.js';
|
||||
import { endpoints } from 'misskey-js/built/endpoints.js';
|
||||
import { endpoints, getEndpointSchema } from 'misskey-js/built/endpoints.js';
|
||||
import type { IEndpointMeta, ResponseOf, SchemaOrUndefined } from 'misskey-js/built/endpoints.types.js';
|
||||
import type { Endpoints } from 'misskey-js';
|
||||
import { WeakSerialized } from 'schema-type';
|
||||
@@ -50,7 +50,8 @@ export abstract class Endpoint<E extends keyof Endpoints, T extends IEndpointMet
|
||||
|
||||
constructor(cb: Executor<T>) {
|
||||
this.meta = endpoints[this.name];
|
||||
const validate = ajv.compile({ oneOf: this.meta.defines.map(d => d.req) });
|
||||
const req = getEndpointSchema('req', this.name);
|
||||
const validate = req ? ajv.compile(req) : null;
|
||||
|
||||
this.exec = (params, user, token, file, ip, headers) => {
|
||||
let cleanup: undefined | (() => void) = undefined;
|
||||
@@ -66,21 +67,27 @@ export abstract class Endpoint<E extends keyof Endpoints, T extends IEndpointMet
|
||||
id: '4267801e-70d1-416a-b011-4ee502885d8b',
|
||||
}));
|
||||
}
|
||||
|
||||
const valid = validate(params);
|
||||
if (!valid) {
|
||||
if (file) cleanup!();
|
||||
|
||||
const errors = validate.errors!;
|
||||
const err = new ApiError({
|
||||
message: 'Invalid param.',
|
||||
code: 'INVALID_PARAM',
|
||||
id: '3d81ceae-475f-4600-b2a8-2bc116157532',
|
||||
}, {
|
||||
param: errors[0].schemaPath,
|
||||
reason: errors[0].message,
|
||||
});
|
||||
return Promise.reject(err);
|
||||
|
||||
if (validate) {
|
||||
const valid = validate(params);
|
||||
|
||||
if (!valid) {
|
||||
if (file) cleanup!();
|
||||
|
||||
const errors = validate.errors!;
|
||||
const err = new ApiError({
|
||||
message: 'Invalid param.',
|
||||
code: 'INVALID_PARAM',
|
||||
id: '3d81ceae-475f-4600-b2a8-2bc116157532',
|
||||
}, {
|
||||
param: errors[0].schemaPath,
|
||||
reason: errors[0].message,
|
||||
});
|
||||
return Promise.reject(err);
|
||||
}
|
||||
} else {
|
||||
// validateがnullである場合、paramsがnullや空オブジェクトであるべきではあるが、
|
||||
// 特にチェックはしない
|
||||
}
|
||||
|
||||
return cb(params as any, user as any, token, file, cleanup, ip, headers);
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import type { Config } from '@/config.js';
|
||||
import endpoints from '../endpoints.js';
|
||||
import { endpoints, getEndpointSchema } from 'misskey-js/built/endpoints.js';
|
||||
import { errors as basicErrors } from './errors.js';
|
||||
import { schemas, convertSchemaToOpenApiSchema } from './schemas.js';
|
||||
import { schemas } from './schemas.js';
|
||||
import { Endpoints } from 'misskey-js';
|
||||
|
||||
export function genOpenapiSpec(config: Config) {
|
||||
const spec = {
|
||||
@@ -37,11 +38,11 @@ export function genOpenapiSpec(config: Config) {
|
||||
},
|
||||
};
|
||||
|
||||
for (const endpoint of endpoints.filter(ep => !ep.meta.secure)) {
|
||||
for (const [name, endpoint] of Object.entries(endpoints).filter(([name, ep]) => !ep.secure)) {
|
||||
const errors = {} as any;
|
||||
|
||||
if (endpoint.meta.errors) {
|
||||
for (const e of Object.values(endpoint.meta.errors)) {
|
||||
if ('errors' in endpoint && endpoint.errors) {
|
||||
for (const e of Object.values(endpoint.errors)) {
|
||||
errors[e.code] = {
|
||||
value: {
|
||||
error: e,
|
||||
@@ -50,42 +51,30 @@ export function genOpenapiSpec(config: Config) {
|
||||
}
|
||||
}
|
||||
|
||||
const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {};
|
||||
const resSchema = getEndpointSchema('res', name as keyof Endpoints);
|
||||
|
||||
let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n';
|
||||
desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`;
|
||||
if (endpoint.meta.kind) {
|
||||
const kind = endpoint.meta.kind;
|
||||
let desc = ('description' in endpoint ? endpoint.description : 'No description provided.') + '\n\n';
|
||||
desc += `**Credential required**: *${('requireCredential' in endpoint && endpoint.requireCredential) ? 'Yes' : 'No'}*`;
|
||||
if ('kind' in endpoint && endpoint.kind) {
|
||||
const kind = endpoint.kind;
|
||||
desc += ` / **Permission**: *${kind}*`;
|
||||
}
|
||||
|
||||
const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json';
|
||||
const schema = { ...endpoint.params };
|
||||
|
||||
if (endpoint.meta.requireFile) {
|
||||
schema.properties = {
|
||||
...schema.properties,
|
||||
file: {
|
||||
type: 'string',
|
||||
format: 'binary',
|
||||
description: 'The file contents.',
|
||||
},
|
||||
};
|
||||
schema.required = [...schema.required ?? [], 'file'];
|
||||
}
|
||||
const requestType = ('requireFile' in endpoint && endpoint.requireFile) ? 'multipart/form-data' : 'application/json';
|
||||
const schema = getEndpointSchema('req', name as keyof Endpoints) ?? {};
|
||||
|
||||
const info = {
|
||||
operationId: endpoint.name,
|
||||
summary: endpoint.name,
|
||||
operationId: name,
|
||||
summary: name,
|
||||
description: desc,
|
||||
externalDocs: {
|
||||
description: 'Source code',
|
||||
url: `https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`,
|
||||
url: `https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/${name}.ts`,
|
||||
},
|
||||
...(endpoint.meta.tags ? {
|
||||
tags: [endpoint.meta.tags[0]],
|
||||
...(('tags' in endpoint && endpoint.tags) ? {
|
||||
tags: [endpoint.tags[0]],
|
||||
} : {}),
|
||||
...(endpoint.meta.requireCredential ? {
|
||||
...('requireCredential' in endpoint && endpoint.requireCredential ? {
|
||||
security: [{
|
||||
ApiKeyAuth: [],
|
||||
}],
|
||||
@@ -99,7 +88,7 @@ export function genOpenapiSpec(config: Config) {
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
...(endpoint.meta.res ? {
|
||||
...(resSchema ? {
|
||||
'200': {
|
||||
description: 'OK (with results)',
|
||||
content: {
|
||||
@@ -157,7 +146,7 @@ export function genOpenapiSpec(config: Config) {
|
||||
},
|
||||
},
|
||||
},
|
||||
...(endpoint.meta.limit ? {
|
||||
...(('limit' in endpoint && endpoint.limit) ? {
|
||||
'429': {
|
||||
description: 'To many requests',
|
||||
content: {
|
||||
@@ -184,7 +173,7 @@ export function genOpenapiSpec(config: Config) {
|
||||
},
|
||||
};
|
||||
|
||||
spec.paths['/' + endpoint.name] = {
|
||||
spec.paths['/' + name] = {
|
||||
post: info,
|
||||
};
|
||||
}
|
||||
|
@@ -1,31 +1,4 @@
|
||||
import type { JSONSchema7 } from 'schema-type';
|
||||
import { refs } from 'misskey-js';
|
||||
|
||||
export function convertSchemaToOpenApiSchema(schema: JSONSchema7) {
|
||||
const res: any = schema;
|
||||
|
||||
if (schema.type === 'object' && schema.properties) {
|
||||
res.required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k);
|
||||
|
||||
for (const k of Object.keys(schema.properties)) {
|
||||
res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]);
|
||||
}
|
||||
}
|
||||
|
||||
if (schema.type === 'array' && schema.items) {
|
||||
res.items = convertSchemaToOpenApiSchema(schema.items);
|
||||
}
|
||||
|
||||
if (schema.anyOf) res.anyOf = schema.anyOf.map(convertSchemaToOpenApiSchema);
|
||||
if (schema.oneOf) res.oneOf = schema.oneOf.map(convertSchemaToOpenApiSchema);
|
||||
if (schema.allOf) res.allOf = schema.allOf.map(convertSchemaToOpenApiSchema);
|
||||
|
||||
if (schema.ref) {
|
||||
res.$ref = `#/components/schemas/${schema.ref}`;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
import { refs } from 'misskey-js/built/schemas.js';
|
||||
|
||||
export const schemas = {
|
||||
Error: {
|
||||
@@ -55,7 +28,5 @@ export const schemas = {
|
||||
required: ['error'],
|
||||
},
|
||||
|
||||
...Object.fromEntries(
|
||||
Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema)]),
|
||||
),
|
||||
...refs,
|
||||
};
|
||||
|
Reference in New Issue
Block a user