Open snakuzzo opened 4 years ago
Hi @snakuzzo! Sure it's possible. You just need a little bit of logic. Something like (this code is untested but hopefully gives you an idea
app.use('/', async (req, res) => {
const [res1, res2, res3] = await Promise.all([
api1.handleRequest(req, req, res),
api2.handleRequest(req, req, res),
api3.handleRequest(req, req, res),
]);
if (res1.statusCode !== 404) return res1;
if (res2.statusCode !== 404) return res2;
if (res3.statusCode !== 404) return res3;
return res.status(404).end();
});
I tried, but it doesn't work. I've got an array of OpenAPIBackend, so this is my code...
app.use('/', async (req, res) => {
const [res1, res2] = await Promise.all([
apiList[0].handleRequest(req, req, res),
apiList[1].handleRequest(req, req, res)
]);
if (res1.statusCode !== 404) return res1;
if (res2.statusCode !== 404) return res2;
return res.status(404).end();
});
but when I try it raises an UnhandledPromiseRejectionWarning
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
One handleRequest OK...
app.use('/', async (req, res) => {
const [res1, res2] = await Promise.all([
apiList[0].handleRequest(req, req, res)
]);
if (res1.statusCode !== 404) return res1;
return res.status(404).end();
});
::1 - - [17/Jun/2020:12:54:27 +0000] "POST /api/myendpoint/111111 HTTP/1.1" 200 217 "-" "PostmanRuntime/7.25.0"
More than one handleRequest KO...
app.use('/', async (req, res) => {
const [res1, res2] = await Promise.all([
apiList[0].handleRequest(req, req, res),
apiList[1].handleRequest(req, req, res)
]);
if (res1.statusCode !== 404) return res1;
if (res2.statusCode !== 404) return res2;
return res.status(404).end();
});
::1 - - [17/Jun/2020:12:56:23 +0000] "POST /api/myendpoint/111111 HTTP/1.1" 404 19 "-" "PostmanRuntime/7.25.0"
(node:31034) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:535:11)
at ServerResponse.header (/home/snakuzzo/workspace/mockserver/node_modules/express/lib/response.js:771:10)
at ServerResponse.send (/home/snakuzzo/workspace/mockserver/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/home/snakuzzo/workspace/mockserver/node_modules/express/lib/response.js:267:15)
at notImplemented (/home/snakuzzo/workspace/mockserver/app.js:98:45)
at OpenAPIBackend.<anonymous> (/home/snakuzzo/workspace/mockserver/node_modules/openapi-backend/backend.js:260:24)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async OpenAPIBackend.handleRequest (/home/snakuzzo/workspace/mockserver/node_modules/openapi-backend/backend.js:161:26)
at async Promise.all (index 0)
at async /home/snakuzzo/workspace/mockserver/app.js:128:30
(node:31034) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:31034) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Tried...
app.use('/', async (req, res) => {
try {
let [res1,res2] = await Promise.all([
apiList[0].handleRequest(req, req, res),
apiList[1].handleRequest(req, req, res)
]);
if (res.statusCode !== 404) return res
return res.status(404).end();
} catch(error) {
console.log(error)
}
});
error...
::1 - - [17/Jun/2020:15:31:03 +0000] "POST /api/myendpoint/111111 HTTP/1.1" 404 19 "-" "PostmanRuntime/7.25.0"
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:535:11)
at ServerResponse.header (/home/snakuzzo/workspace/mockserver/node_modules/express/lib/response.js:771:10)
at ServerResponse.send (/home/snakuzzo/workspace/mockserver/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/home/snakuzzo/workspace/mockserver/node_modules/express/lib/response.js:267:15)
at notImplemented (/home/snakuzzo/workspace/mockserver/app.js:98:45)
at OpenAPIBackend.<anonymous> (/home/snakuzzo/workspace/mockserver/node_modules/openapi-backend/backend.js:260:24)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async OpenAPIBackend.handleRequest (/home/snakuzzo/workspace/mockserver/node_modules/openapi-backend/backend.js:161:26)
at async Promise.all (index 0)
at async /home/snakuzzo/workspace/mockserver/app.js:131:29 {
code: 'ERR_HTTP_HEADERS_SENT'
I'm stuck... :(
Here a full example to reproduce the error... any solution?
const OpenAPIBackend = require('openapi-backend').default;
const express = require('express');
const app = express();
app.use(express.json());
// define api
const api1 = new OpenAPIBackend({
definition: {
openapi: '3.0.1',
info: {
title: 'My API',
version: '1.0.0',
},
paths: {
'/pets': {
get: {
operationId: 'getPets',
responses: {
200: { description: 'ok' },
},
},
},
'/pets/{id}': {
get: {
operationId: 'getPetById',
responses: {
200: { description: 'ok' },
},
},
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: {
type: 'integer',
},
},
],
},
},
},
handlers: {
getPets: async (c, req, res) => res.status(200).json({ operationId: c.operation.operationId }),
getPetById: async (c, req, res) => res.status(200).json({ operationId: c.operation.operationId }),
validationFail: async (c, req, res) => res.status(400).json({ err: c.validation.errors }),
notFound: async (c, req, res) => res.status(404).json({ err: 'not found' }),
notImplemented: async (c, req, res) => {
const { status, mock } = c.api.mockResponseForOperation(c.operation.operationId);
return res.status(status).json(mock);
},
},
});
const api2 = new OpenAPIBackend({
definition: {
openapi: '3.0.1',
info: {
title: 'My Other API',
version: '1.0.0',
},
paths: {
'/pets1': {
get: {
operationId: 'getPets',
responses: {
200: { description: 'ok' },
},
},
},
'/pets1/{id}': {
get: {
operationId: 'getPetById',
responses: {
200: { description: 'ok' },
},
},
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: {
type: 'integer',
},
},
],
},
},
},
handlers: {
getPets: async (c, req, res) => res.status(200).json({ operationId: c.operation.operationId }),
getPetById: async (c, req, res) => res.status(200).json({ operationId: c.operation.operationId }),
validationFail: async (c, req, res) => res.status(400).json({ err: c.validation.errors }),
notFound: async (c, req, res) => res.status(404).json({ err: 'not found' }),
notImplemented: async (c, req, res) => {
const { status, mock } = c.api.mockResponseForOperation(c.operation.operationId);
return res.status(status).json(mock);
},
},
});
api1.init();
api2.init();
// use as express middleware
//app.use((req, res) => api.handleRequest(req, req, res));
app.use('/', async (req, res) => {
try {
let [res1,res2] = await Promise.all([
api1.handleRequest(req, req, res),
api2.handleRequest(req, req, res)
]);
if (res1.statusCode !== 404) return res1.end();
if (res2.statusCode !== 404) return res2.end();
return res.status(404).end();
} catch(error) {
console.log(error)
}
});
// start server
app.listen(8080, () => console.info('api listening at http://localhost:8080'));
@snakuzzo You can solve this with something like
const OpenAPIBackend = require('openapi-backend').default;
const express = require('express');
const { STATUS_CODES } = require("http");
const app = express();
app.use(express.json());
// define api
const api1 = new OpenAPIBackend({
definition: {
openapi: '3.0.1',
info: {
title: 'My API',
version: '1.0.0',
},
paths: {
'/pets': {
get: {
operationId: 'getPets',
responses: {
200: { description: 'ok' },
},
},
},
'/pets/{id}': {
get: {
operationId: 'getPetById',
responses: {
200: { description: 'ok' },
},
},
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: {
type: 'integer',
},
},
],
},
},
},
handlers: {
getPets: async (c, req, res) => res.status(200).json({ operationId: c.operation.operationId }),
getPetById: async (c, req, res) => res.status(200).json({ operationId: c.operation.operationId }),
validationFail: async (c, req, res) => res.status(400).json({ err: c.validation.errors }),
notFound: async (c, req, res) => res.status(404).json({ err: 'not found' }),
notImplemented: async (c, req, res) => {
const { status, mock } = c.api.mockResponseForOperation(c.operation.operationId);
return res.status(status).json(mock);
},
},
});
const api2 = new OpenAPIBackend({
definition: {
openapi: '3.0.1',
info: {
title: 'My Other API',
version: '1.0.0',
},
paths: {
'/pets1': {
get: {
operationId: 'getPets',
responses: {
200: { description: 'ok' },
},
},
},
'/pets1/{id}': {
get: {
operationId: 'getPetById',
responses: {
200: { description: 'ok' },
},
},
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: {
type: 'integer',
},
},
],
},
},
},
handlers: {
getPets: async (c, req, res) => res.status(200).json({ operationId: c.operation.operationId }),
getPetById: async (c, req, res) => res.status(200).json({ operationId: c.operation.operationId }),
validationFail: async (c, req, res) => res.status(400).json({ err: c.validation.errors }),
notFound: async (c, req, res) => res.status(404).json({ err: 'not found' }),
notImplemented: async (c, req, res) => {
const { status, mock } = c.api.mockResponseForOperation(c.operation.operationId);
return res.status(status).json(mock);
},
},
});
api1.init();
api2.init();
// use as express middleware
//app.use((req, res) => api.handleRequest(req, req, res));
app.use(async (req, res, next) => {
try {
const match = [api1, api2].filter(
(v) => v.matchOperation(req, false) !== undefined
);
switch (match.length) {
case 0:
//Not found in any api
next();
break;
case 1:
match[0].handleRequest(req, req, res).catch(next);
break;
default:
next({
status: 500,
message: `Possible operationId collision (${match
.map((v) => v.definition.info.title)
.join(", ")})`,
});
}
} catch (error) {
next(error);
}
});
app.use("*", (req, res) => {
res.status(404).json({
message: `${req.baseUrl || req.originalUrl} not found`,
code: STATUS_CODE[404],
});
});
app.use((err, req, res, next) => {
const status = err.status || 500;
if (status >= 500) {
console.error(err);
}
res.status(status).json({
message: err.message,
errors: err.errors,
code: STATUS_CODES[status] || "Internal server error",
});
});
// start server
app.listen(8080, () => console.info('api listening at http://localhost:8080'));
Hi, Maybe it's not possible, but I'm trying to serve multiple mocks under same path but without success. I have multiple openapi spec files and I need to create a unique mock server The only way I found is to use different routes like this:
app.use('/' + spec + '/', (req, res) => api.handleRequest(req, req, res));
where spec is the name of every single spec. And the result is:
Is there a way to serve all under same path (eg.
/
) ?Thank you