mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-21 16:06:16 -04:00
openapi: add documentation, small optimisation
This commit is contained in:
parent
c2cca39c7d
commit
e821bbcad8
5 changed files with 154 additions and 180 deletions
|
@ -111,6 +111,8 @@ For **responsible disclosure of vulnerabilities**, please write a mail to the ma
|
||||||
Etherpad is designed to be easily embeddable and provides a [HTTP API](https://github.com/ether/etherpad-lite/wiki/HTTP-API)
|
Etherpad is designed to be easily embeddable and provides a [HTTP API](https://github.com/ether/etherpad-lite/wiki/HTTP-API)
|
||||||
that allows your web application to manage pads, users and groups. It is recommended to use the [available client implementations](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) in order to interact with this API.
|
that allows your web application to manage pads, users and groups. It is recommended to use the [available client implementations](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) in order to interact with this API.
|
||||||
|
|
||||||
|
OpenAPI (previously swagger) definitions for the API are exposed under `/api/openapi.json`.
|
||||||
|
|
||||||
# jQuery plugin
|
# jQuery plugin
|
||||||
There is a [jQuery plugin](https://github.com/ether/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website.
|
There is a [jQuery plugin](https://github.com/ether/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website.
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,10 @@ The API is designed in a way, so you can reuse your existing user system with th
|
||||||
|
|
||||||
Take a look at [HTTP API client libraries](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) to check if a library in your favorite programming language is available.
|
Take a look at [HTTP API client libraries](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) to check if a library in your favorite programming language is available.
|
||||||
|
|
||||||
|
### OpenAPI
|
||||||
|
|
||||||
|
OpenAPI (formerly swagger) definitions are exposed under `/api/openapi.json` (latest) and `/api/{version}/openapi.json`. You can use official tools like [Swagger Editor](https://editor.swagger.io/) to view and explore them.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Example 1
|
### Example 1
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
|
/**
|
||||||
|
* node/hooks/express/openapi.js
|
||||||
|
*
|
||||||
|
* This module generates OpenAPI definitions for each API version defined by
|
||||||
|
* APIHandler.js and hooks into express to route the API using openapi-backend.
|
||||||
|
*
|
||||||
|
* The openapi definition files are publicly available under:
|
||||||
|
*
|
||||||
|
* - /api/openapi.json
|
||||||
|
* - /rest/openapi.json
|
||||||
|
* - /api/{version}/openapi.json
|
||||||
|
* - /rest/{version}/openapi.json
|
||||||
|
*/
|
||||||
|
|
||||||
const OpenAPIBackend = require('openapi-backend').default;
|
const OpenAPIBackend = require('openapi-backend').default;
|
||||||
const formidable = require('formidable');
|
const formidable = require('formidable');
|
||||||
const { promisify } = require('util');
|
const { promisify } = require('util');
|
||||||
|
const cloneDeep = require('lodash.clonedeep');
|
||||||
|
|
||||||
const apiHandler = require('../../handler/APIHandler');
|
const apiHandler = require('../../handler/APIHandler');
|
||||||
const settings = require('../../utils/Settings');
|
const settings = require('../../utils/Settings');
|
||||||
|
@ -33,232 +48,203 @@ const APIPathStyle = {
|
||||||
REST: 'rest', // restful paths e.g. /rest/group/create
|
REST: 'rest', // restful paths e.g. /rest/group/create
|
||||||
};
|
};
|
||||||
|
|
||||||
function sessionListResponseProcessor(res) {
|
// API resources - describe your API endpoints here
|
||||||
if (res.data) {
|
|
||||||
var sessions = [];
|
|
||||||
for (var sessionId in res.data) {
|
|
||||||
var sessionInfo = res.data[sessionId];
|
|
||||||
sessionId['id'] = sessionId;
|
|
||||||
sessions.push(sessionInfo);
|
|
||||||
}
|
|
||||||
res.data = sessions;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// API resources
|
|
||||||
// add your operations here
|
|
||||||
const resources = {
|
const resources = {
|
||||||
// Group
|
// Group
|
||||||
group: {
|
group: {
|
||||||
create: {
|
create: {
|
||||||
func: 'createGroup',
|
operationId: 'createGroup',
|
||||||
description: 'creates a new group',
|
summary: 'creates a new group',
|
||||||
response: { groupID: { type: 'string' } },
|
responseSchema: { groupID: { type: 'string' } },
|
||||||
},
|
},
|
||||||
createIfNotExistsFor: {
|
createIfNotExistsFor: {
|
||||||
func: 'createGroupIfNotExistsFor',
|
operationId: 'createGroupIfNotExistsFor',
|
||||||
description: 'this functions helps you to map your application group ids to Etherpad group ids',
|
summary: 'this functions helps you to map your application group ids to Etherpad group ids',
|
||||||
response: { groupID: { type: 'string' } },
|
responseSchema: { groupID: { type: 'string' } },
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
func: 'deleteGroup',
|
operationId: 'deleteGroup',
|
||||||
description: 'deletes a group',
|
summary: 'deletes a group',
|
||||||
},
|
},
|
||||||
listPads: {
|
listPads: {
|
||||||
func: 'listPads',
|
operationId: 'listPads',
|
||||||
description: 'returns all pads of this group',
|
summary: 'returns all pads of this group',
|
||||||
response: { padIDs: { type: 'array', items: { type: 'string' } } },
|
responseSchema: { padIDs: { type: 'array', items: { type: 'string' } } },
|
||||||
},
|
},
|
||||||
createPad: {
|
createPad: {
|
||||||
func: 'createGroupPad',
|
operationId: 'createGroupPad',
|
||||||
description: 'creates a new pad in this group',
|
summary: 'creates a new pad in this group',
|
||||||
},
|
},
|
||||||
listSessions: {
|
listSessions: {
|
||||||
func: 'listSessionsOfGroup',
|
operationId: 'listSessionsOfGroup',
|
||||||
description: '',
|
summary: '',
|
||||||
response: { sessions: { type: 'array', items: { $ref: '#/components/schemas/SessionInfo' } } },
|
responseSchema: { sessions: { type: 'array', items: { $ref: '#/components/schemas/SessionInfo' } } },
|
||||||
responseProcessor: sessionListResponseProcessor,
|
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
func: 'listAllGroups',
|
operationId: 'listAllGroups',
|
||||||
description: '',
|
summary: '',
|
||||||
response: { groupIDs: { type: 'array', items: { type: 'string' } } },
|
responseSchema: { groupIDs: { type: 'array', items: { type: 'string' } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Author
|
// Author
|
||||||
author: {
|
author: {
|
||||||
create: {
|
create: {
|
||||||
func: 'createAuthor',
|
operationId: 'createAuthor',
|
||||||
description: 'creates a new author',
|
summary: 'creates a new author',
|
||||||
response: { authorID: { type: 'string' } },
|
responseSchema: { authorID: { type: 'string' } },
|
||||||
},
|
},
|
||||||
createIfNotExistsFor: {
|
createIfNotExistsFor: {
|
||||||
func: 'createAuthorIfNotExistsFor',
|
operationId: 'createAuthorIfNotExistsFor',
|
||||||
description: 'this functions helps you to map your application author ids to Etherpad author ids',
|
summary: 'this functions helps you to map your application author ids to Etherpad author ids',
|
||||||
response: { authorID: { type: 'string' } },
|
responseSchema: { authorID: { type: 'string' } },
|
||||||
},
|
},
|
||||||
listPads: {
|
listPads: {
|
||||||
func: 'listPadsOfAuthor',
|
operationId: 'listPadsOfAuthor',
|
||||||
description: 'returns an array of all pads this author contributed to',
|
summary: 'returns an array of all pads this author contributed to',
|
||||||
response: { padIDs: { type: 'array', items: { type: 'string' } } },
|
responseSchema: { padIDs: { type: 'array', items: { type: 'string' } } },
|
||||||
},
|
},
|
||||||
listSessions: {
|
listSessions: {
|
||||||
func: 'listSessionsOfAuthor',
|
operationId: 'listSessionsOfAuthor',
|
||||||
description: 'returns all sessions of an author',
|
summary: 'returns all sessions of an author',
|
||||||
response: { sessions: { type: 'array', items: { $ref: '#/components/schemas/SessionInfo' } } },
|
responseSchema: { sessions: { type: 'array', items: { $ref: '#/components/schemas/SessionInfo' } } },
|
||||||
responseProcessor: sessionListResponseProcessor,
|
|
||||||
},
|
},
|
||||||
// We need an operation that return a UserInfo so it can be picked up by the codegen :(
|
// We need an operation that return a UserInfo so it can be picked up by the codegen :(
|
||||||
getName: {
|
getName: {
|
||||||
func: 'getAuthorName',
|
operationId: 'getAuthorName',
|
||||||
description: 'Returns the Author Name of the author',
|
summary: 'Returns the Author Name of the author',
|
||||||
responseProcessor: function(response) {
|
responseSchema: { info: { $ref: '#/components/schemas/UserInfo' } },
|
||||||
if (response.data) {
|
|
||||||
response['info'] = { name: response.data.authorName };
|
|
||||||
delete response['data'];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
response: { info: { type: 'UserInfo' } },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Session
|
// Session
|
||||||
session: {
|
session: {
|
||||||
create: {
|
create: {
|
||||||
func: 'createSession',
|
operationId: 'createSession',
|
||||||
description: 'creates a new session. validUntil is an unix timestamp in seconds',
|
summary: 'creates a new session. validUntil is an unix timestamp in seconds',
|
||||||
response: { sessionID: { type: 'string' } },
|
responseSchema: { sessionID: { type: 'string' } },
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
func: 'deleteSession',
|
operationId: 'deleteSession',
|
||||||
description: 'deletes a session',
|
summary: 'deletes a session',
|
||||||
},
|
},
|
||||||
// We need an operation that returns a SessionInfo so it can be picked up by the codegen :(
|
// We need an operation that returns a SessionInfo so it can be picked up by the codegen :(
|
||||||
info: {
|
info: {
|
||||||
func: 'getSessionInfo',
|
operationId: 'getSessionInfo',
|
||||||
description: 'returns informations about a session',
|
summary: 'returns informations about a session',
|
||||||
response: { info: { $ref: '#/components/schemas/SessionInfo' } },
|
responseSchema: { info: { $ref: '#/components/schemas/SessionInfo' } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Pad
|
// Pad
|
||||||
pad: {
|
pad: {
|
||||||
listAll: {
|
listAll: {
|
||||||
func: 'listAllPads',
|
operationId: 'listAllPads',
|
||||||
description: 'list all the pads',
|
summary: 'list all the pads',
|
||||||
response: { padIDs: { type: 'array', items: { type: 'string' } } },
|
responseSchema: { padIDs: { type: 'array', items: { type: 'string' } } },
|
||||||
},
|
},
|
||||||
createDiffHTML: {
|
createDiffHTML: {
|
||||||
func: 'createDiffHTML',
|
operationId: 'createDiffHTML',
|
||||||
description: '',
|
summary: '',
|
||||||
response: {},
|
responseSchema: {},
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
func: 'createPad',
|
operationId: 'createPad',
|
||||||
description:
|
description:
|
||||||
'creates a new (non-group) pad. Note that if you need to create a group Pad, you should call createGroupPad',
|
'creates a new (non-group) pad. Note that if you need to create a group Pad, you should call createGroupPad',
|
||||||
},
|
},
|
||||||
getText: {
|
getText: {
|
||||||
func: 'getText',
|
operationId: 'getText',
|
||||||
description: 'returns the text of a pad',
|
summary: 'returns the text of a pad',
|
||||||
response: { text: { type: 'string' } },
|
responseSchema: { text: { type: 'string' } },
|
||||||
},
|
},
|
||||||
setText: {
|
setText: {
|
||||||
func: 'setText',
|
operationId: 'setText',
|
||||||
description: 'sets the text of a pad',
|
summary: 'sets the text of a pad',
|
||||||
},
|
},
|
||||||
getHTML: {
|
getHTML: {
|
||||||
func: 'getHTML',
|
operationId: 'getHTML',
|
||||||
description: 'returns the text of a pad formatted as HTML',
|
summary: 'returns the text of a pad formatted as HTML',
|
||||||
response: { html: { type: 'string' } },
|
responseSchema: { html: { type: 'string' } },
|
||||||
},
|
},
|
||||||
setHTML: {
|
setHTML: {
|
||||||
func: 'setHTML',
|
operationId: 'setHTML',
|
||||||
description: 'sets the text of a pad with HTML',
|
summary: 'sets the text of a pad with HTML',
|
||||||
},
|
},
|
||||||
getRevisionsCount: {
|
getRevisionsCount: {
|
||||||
func: 'getRevisionsCount',
|
operationId: 'getRevisionsCount',
|
||||||
description: 'returns the number of revisions of this pad',
|
summary: 'returns the number of revisions of this pad',
|
||||||
response: { revisions: { type: 'integer' } },
|
responseSchema: { revisions: { type: 'integer' } },
|
||||||
},
|
},
|
||||||
getLastEdited: {
|
getLastEdited: {
|
||||||
func: 'getLastEdited',
|
operationId: 'getLastEdited',
|
||||||
description: 'returns the timestamp of the last revision of the pad',
|
summary: 'returns the timestamp of the last revision of the pad',
|
||||||
response: { lastEdited: { type: 'integer' } },
|
responseSchema: { lastEdited: { type: 'integer' } },
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
func: 'deletePad',
|
operationId: 'deletePad',
|
||||||
description: 'deletes a pad',
|
summary: 'deletes a pad',
|
||||||
},
|
},
|
||||||
getReadOnlyID: {
|
getReadOnlyID: {
|
||||||
func: 'getReadOnlyID',
|
operationId: 'getReadOnlyID',
|
||||||
description: 'returns the read only link of a pad',
|
summary: 'returns the read only link of a pad',
|
||||||
response: { readOnlyID: { type: 'string' } },
|
responseSchema: { readOnlyID: { type: 'string' } },
|
||||||
},
|
},
|
||||||
setPublicStatus: {
|
setPublicStatus: {
|
||||||
func: 'setPublicStatus',
|
operationId: 'setPublicStatus',
|
||||||
description: 'sets a boolean for the public status of a pad',
|
summary: 'sets a boolean for the public status of a pad',
|
||||||
},
|
},
|
||||||
getPublicStatus: {
|
getPublicStatus: {
|
||||||
func: 'getPublicStatus',
|
operationId: 'getPublicStatus',
|
||||||
description: 'return true of false',
|
summary: 'return true of false',
|
||||||
response: { publicStatus: { type: 'boolean' } },
|
responseSchema: { publicStatus: { type: 'boolean' } },
|
||||||
},
|
},
|
||||||
setPassword: {
|
setPassword: {
|
||||||
func: 'setPassword',
|
operationId: 'setPassword',
|
||||||
description: 'returns ok or a error message',
|
summary: 'returns ok or a error message',
|
||||||
},
|
},
|
||||||
isPasswordProtected: {
|
isPasswordProtected: {
|
||||||
func: 'isPasswordProtected',
|
operationId: 'isPasswordProtected',
|
||||||
description: 'returns true or false',
|
summary: 'returns true or false',
|
||||||
response: { passwordProtection: { type: 'boolean' } },
|
responseSchema: { passwordProtection: { type: 'boolean' } },
|
||||||
},
|
},
|
||||||
authors: {
|
authors: {
|
||||||
func: 'listAuthorsOfPad',
|
operationId: 'listAuthorsOfPad',
|
||||||
description: 'returns an array of authors who contributed to this pad',
|
summary: 'returns an array of authors who contributed to this pad',
|
||||||
response: { authorIDs: { type: 'array', items: { type: 'string' } } },
|
responseSchema: { authorIDs: { type: 'array', items: { type: 'string' } } },
|
||||||
},
|
},
|
||||||
usersCount: {
|
usersCount: {
|
||||||
func: 'padUsersCount',
|
operationId: 'padUsersCount',
|
||||||
description: 'returns the number of user that are currently editing this pad',
|
summary: 'returns the number of user that are currently editing this pad',
|
||||||
response: { padUsersCount: { type: 'integer' } },
|
responseSchema: { padUsersCount: { type: 'integer' } },
|
||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
func: 'padUsers',
|
operationId: 'padUsers',
|
||||||
description: 'returns the list of users that are currently editing this pad',
|
summary: 'returns the list of users that are currently editing this pad',
|
||||||
response: { padUsers: { type: 'array', items: { $ref: '#/components/schemas/UserInfo' } } },
|
responseSchema: { padUsers: { type: 'array', items: { $ref: '#/components/schemas/UserInfo' } } },
|
||||||
},
|
},
|
||||||
sendClientsMessage: {
|
sendClientsMessage: {
|
||||||
func: 'sendClientsMessage',
|
operationId: 'sendClientsMessage',
|
||||||
description: 'sends a custom message of type msg to the pad',
|
summary: 'sends a custom message of type msg to the pad',
|
||||||
},
|
},
|
||||||
checkToken: {
|
checkToken: {
|
||||||
func: 'checkToken',
|
operationId: 'checkToken',
|
||||||
description: 'returns ok when the current api token is valid',
|
summary: 'returns ok when the current api token is valid',
|
||||||
},
|
},
|
||||||
getChatHistory: {
|
getChatHistory: {
|
||||||
func: 'getChatHistory',
|
operationId: 'getChatHistory',
|
||||||
description: 'returns the chat history',
|
summary: 'returns the chat history',
|
||||||
response: { messages: { type: 'array', items: { $ref: '#/components/schemas/Message' } } },
|
responseSchema: { messages: { type: 'array', items: { $ref: '#/components/schemas/Message' } } },
|
||||||
},
|
},
|
||||||
// We need an operation that returns a Message so it can be picked up by the codegen :(
|
// We need an operation that returns a Message so it can be picked up by the codegen :(
|
||||||
getChatHead: {
|
getChatHead: {
|
||||||
func: 'getChatHead',
|
operationId: 'getChatHead',
|
||||||
description: 'returns the chatHead (chat-message) of the pad',
|
summary: 'returns the chatHead (chat-message) of the pad',
|
||||||
responseProcessor: function(response) {
|
responseSchema: { chatHead: { $ref: '#/components/schemas/Message' } },
|
||||||
// move this to info
|
|
||||||
if (response.data) {
|
|
||||||
response['chatHead'] = { time: response.data['chatHead'] };
|
|
||||||
delete response['data'];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
response: { chatHead: { type: 'Message' } },
|
|
||||||
},
|
},
|
||||||
appendChatMessage: {
|
appendChatMessage: {
|
||||||
func: 'appendChatMessage',
|
operationId: 'appendChatMessage',
|
||||||
description: 'appends a chat message',
|
summary: 'appends a chat message',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -401,50 +387,30 @@ const defaultResponseRefs = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// convert to a flat list of OAS Operation objects
|
// convert to a dictionary of operation objects
|
||||||
const operations = [];
|
const operations = {};
|
||||||
const responseProcessors = {};
|
|
||||||
for (const resource in resources) {
|
for (const resource in resources) {
|
||||||
for (const action in resources[resource]) {
|
for (const action in resources[resource]) {
|
||||||
const { func: operationId, description, response, responseProcessor } = resources[resource][action];
|
const { operationId, responseSchema, ...operation } = resources[resource][action];
|
||||||
|
|
||||||
|
// add response objects
|
||||||
const responses = { ...defaultResponseRefs };
|
const responses = { ...defaultResponseRefs };
|
||||||
if (response) {
|
if (responseSchema) {
|
||||||
responses[200] = {
|
responses[200] = cloneDeep(defaultResponses.Success);
|
||||||
description: 'ok (code 0)',
|
responses[200].content['application/json'].schema.properties.data = {
|
||||||
content: {
|
type: 'object',
|
||||||
'application/json': {
|
properties: responseSchema,
|
||||||
schema: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
code: {
|
|
||||||
type: 'integer',
|
|
||||||
example: 0,
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
type: 'string',
|
|
||||||
example: 'ok',
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
type: 'object',
|
|
||||||
properties: response,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const operation = {
|
// add final operation object to dictionary
|
||||||
|
operations[operationId] = {
|
||||||
operationId,
|
operationId,
|
||||||
summary: description,
|
...operation,
|
||||||
responses,
|
responses,
|
||||||
tags: [resource],
|
tags: [resource],
|
||||||
_restPath: `/${resource}/${action}`,
|
_restPath: `/${resource}/${action}`,
|
||||||
_responseProcessor: responseProcessor,
|
|
||||||
};
|
};
|
||||||
operations[operationId] = operation;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,10 +523,6 @@ const generateDefinitionForVersion = (version, style = APIPathStyle.FLAT) => {
|
||||||
}
|
}
|
||||||
delete operation._restPath;
|
delete operation._restPath;
|
||||||
|
|
||||||
// set up response processor
|
|
||||||
responseProcessors[funcName] = operation._responseProcessor;
|
|
||||||
delete operation._responseProcessor;
|
|
||||||
|
|
||||||
// add to definition
|
// add to definition
|
||||||
// NOTE: It may be confusing that every operation can be called with both GET and POST
|
// NOTE: It may be confusing that every operation can be called with both GET and POST
|
||||||
definition.paths[path] = {
|
definition.paths[path] = {
|
||||||
|
@ -574,29 +536,32 @@ const generateDefinitionForVersion = (version, style = APIPathStyle.FLAT) => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return definition;
|
return definition;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.expressCreateServer = (_, args) => {
|
exports.expressCreateServer = async (_, args) => {
|
||||||
const { app } = args;
|
const { app } = args;
|
||||||
|
|
||||||
|
// create openapi-backend handlers for each api version under /api/{version}/*
|
||||||
for (const version in apiHandler.version) {
|
for (const version in apiHandler.version) {
|
||||||
// create two different styles of api: flat + rest
|
// we support two different styles of api: flat + rest
|
||||||
|
// TODO: do we really want to support both?
|
||||||
|
|
||||||
for (const style of [APIPathStyle.FLAT, APIPathStyle.REST]) {
|
for (const style of [APIPathStyle.FLAT, APIPathStyle.REST]) {
|
||||||
const apiRoot = getApiRootForVersion(version, style);
|
const apiRoot = getApiRootForVersion(version, style);
|
||||||
|
|
||||||
// generate openapi definition for this API version
|
// generate openapi definition for this API version
|
||||||
const definition = generateDefinitionForVersion(version, style);
|
const definition = generateDefinitionForVersion(version, style);
|
||||||
|
|
||||||
// serve openapi definition file
|
// serve version specific openapi definition
|
||||||
app.get(`${apiRoot}/openapi.json`, (req, res) => {
|
app.get(`${apiRoot}/openapi.json`, (req, res) => {
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
res.json({ ...definition, servers: [generateServerForApiVersion(apiRoot, req)] });
|
res.json({ ...definition, servers: [generateServerForApiVersion(apiRoot, req)] });
|
||||||
});
|
});
|
||||||
|
|
||||||
// serve latest openapi definition file under /api/openapi.json
|
// serve latest openapi definition file under /api/openapi.json
|
||||||
if (version === apiHandler.latestApiVersion) {
|
const isLatestAPIVersion = version === apiHandler.latestApiVersion;
|
||||||
|
if (isLatestAPIVersion) {
|
||||||
app.get(`/${style}/openapi.json`, (req, res) => {
|
app.get(`/${style}/openapi.json`, (req, res) => {
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
res.json({ ...definition, servers: [generateServerForApiVersion(apiRoot, req)] });
|
res.json({ ...definition, servers: [generateServerForApiVersion(apiRoot, req)] });
|
||||||
|
@ -605,10 +570,12 @@ exports.expressCreateServer = (_, args) => {
|
||||||
|
|
||||||
// build openapi-backend instance for this api version
|
// build openapi-backend instance for this api version
|
||||||
const api = new OpenAPIBackend({
|
const api = new OpenAPIBackend({
|
||||||
apiRoot,
|
apiRoot, // each api version has its own root
|
||||||
definition,
|
definition,
|
||||||
validate: false,
|
validate: false,
|
||||||
quick: true, // recommended when running multiple instances in parallel
|
// for a small optimisation, we can run the quick startup for older
|
||||||
|
// API versions since they are subsets of the latest api definition
|
||||||
|
quick: !isLatestAPIVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
// register default handlers
|
// register default handlers
|
||||||
|
@ -629,6 +596,7 @@ exports.expressCreateServer = (_, args) => {
|
||||||
// parse fields from request
|
// parse fields from request
|
||||||
const { header, params, query } = c.request;
|
const { header, params, query } = c.request;
|
||||||
|
|
||||||
|
// read form data if method was POST
|
||||||
let formData = {};
|
let formData = {};
|
||||||
if (c.request.method === 'post') {
|
if (c.request.method === 'post') {
|
||||||
const form = new formidable.IncomingForm();
|
const form = new formidable.IncomingForm();
|
||||||
|
@ -650,12 +618,6 @@ exports.expressCreateServer = (_, args) => {
|
||||||
// return in common format
|
// return in common format
|
||||||
const response = { code: 0, message: 'ok', data };
|
const response = { code: 0, message: 'ok', data };
|
||||||
|
|
||||||
// NOTE: the original swagger implementation had response processors, but the tests
|
|
||||||
// clearly assume the processors are turned off
|
|
||||||
/*if (responseProcessors[funcName]) {
|
|
||||||
response = responseProcessors[funcName](response);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// log response
|
// log response
|
||||||
apiLogger.info(`RESPONSE, ${funcName}, ${JSON.stringify(response)}`);
|
apiLogger.info(`RESPONSE, ${funcName}, ${JSON.stringify(response)}`);
|
||||||
|
|
||||||
|
|
5
src/package-lock.json
generated
5
src/package-lock.json
generated
|
@ -2422,6 +2422,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
|
||||||
"integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
|
"integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
|
||||||
},
|
},
|
||||||
|
"lodash.clonedeep": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||||
|
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
|
||||||
|
},
|
||||||
"lodash.defaults": {
|
"lodash.defaults": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
"graceful-fs": "4.2.2",
|
"graceful-fs": "4.2.2",
|
||||||
"jsonminify": "0.4.1",
|
"jsonminify": "0.4.1",
|
||||||
"languages4translatewiki": "0.1.3",
|
"languages4translatewiki": "0.1.3",
|
||||||
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"log4js": "0.6.35",
|
"log4js": "0.6.35",
|
||||||
"measured-core": "1.11.2",
|
"measured-core": "1.11.2",
|
||||||
"nodeify": "^1.0.1",
|
"nodeify": "^1.0.1",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue