Hello there!
I am using the express-openapi module v7.2.0 on nodejs v12.18.2. The OS of the computer I am running it on is Windows Server 2019 Datacenter edition. Whenever I attempt to use an OpenAPI v3 yaml file, express-openapi throws validation errors that shows it is validating it against the swagger v2 specification. This is my debug file: 2020-12-21T21_54_01_145Z-debug.log
Here is my yaml file:
---
openapi: 3.0.1
info:
title: Lorem Ipsum
description: Lorem Ipsum
version: 1.0.0
servers:
- url: /info/api
tags:
- name: heartbeat
description: Heartbeat related endpoints
paths:
/heartbeat:
get:
tags:
- heartbeat
summary: Send a heartbeat to the system
description: Send a heartbeat to the system to report an action
operationId: heartbeat
responses:
200:
description: Successfully processed heartbeat.
content:
application/json:
schema:
$ref: '#/components/schemas/Success'
400:
description: Unable to process heartbeat.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
401:
description: User unauthorized to access the API.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-swagger-router-controller: mainController
/saveRequest:
post:
summary: Lorem Ipsum
description: Lorem Ipsum
operationId: saveRequest
parameters:
- name: usertoken
in: header
description: The API token issued to you in order to consume the protected
endpoints
required: true
schema:
type: string
requestBody:
description: Payload to save the incoming flag request
content:
'*/*':
schema:
$ref: '#/components/schemas/SaveRequest'
required: false
responses:
200:
description: Successfully saved the request into the DB.
content:
application/json:
schema:
$ref: '#/components/schemas/Success'
400:
description: Failed to save the request in the DB.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
401:
description: User unauthorized to access the API.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-swagger-router-controller: mainController
x-codegen-request-body-name: SaveRequest
/markActionExecuted:
post:
summary: Marks the action as executed for a given request.
description: Lorem Ipsum
operationId: markActionExecuted
parameters:
- name: usertoken
in: header
description: The API token issued to you in order to consume the protected
endpoints
required: true
schema:
type: string
requestBody:
description: Payload to mark action as executed.
content:
'*/*':
schema:
$ref: '#/components/schemas/MarkActionExecuted'
required: false
responses:
200:
description: Successfully marked action as executed.
content:
application/json:
schema:
$ref: '#/components/schemas/Success'
401:
description: User unauthorized to access the API.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
500:
description: Error executing stored procedure to mark action executed.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-swagger-router-controller: mainController
x-codegen-request-body-name: MarkActionExecuted
/saveAccountInfo:
post:
summary: Lorem Ipsum
description: Lorem Ipsum
operationId: saveAccountInfo
parameters:
- name: usertoken
in: header
description: The API token issued to you in order to consume the protected
endpoints
required: true
schema:
type: string
requestBody:
description: Payload to save the account info
content:
'*/*':
schema:
$ref: '#/components/schemas/AccountInfo'
required: false
responses:
200:
description: Successfully saved the account info in the DB.
content:
application/json:
schema:
$ref: '#/components/schemas/Success'
400:
description: Failed to save the account info in the DB.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
401:
description: User unauthorized to access the API.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-swagger-router-controller: mainController
x-codegen-request-body-name: AccountInfo
/forward:
post:
summary: Lorem Ipsum
description: Lorem Ipsum
operationId: forwardRequest
parameters:
- name: usertoken
in: header
description: The API token issued to you in order to consume the protected
endpoints
required: true
schema:
type: string
requestBody:
description: Payload used when doing forwarding checks
content:
'*/*':
schema:
$ref: '#/components/schemas/Forward'
required: false
responses:
200:
description: Successfully made forwarding decision.
content:
application/json:
schema:
$ref: '#/components/schemas/Success'
400:
description: Bad request from client.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
401:
description: User unauthorized to access the API.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-swagger-router-controller: mainController
x-codegen-request-body-name: Forward
/getUserAuthInfo:
post:
summary: Lorem Ipsum
description: Lorem Ipsum
operationId: getUserAuthInfo
parameters:
- name: usertoken
in: header
description: The API token issued to authenticate and authorize the protected
endpoint
required: true
schema:
type: string
requestBody:
description: Payload with username
content:
'*/*':
schema:
$ref: '#/components/schemas/UserAuthInfo'
required: false
responses:
200:
description: Successfully retrieved user information
content:
application/json:
schema:
$ref: '#/components/schemas/Success'
400:
description: Bad request from client
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
401:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
404:
description: User not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-swagger-router-controller: mainController
x-codegen-request-body-name: UserAuthInfo
/getAccountsProcessed:
post:
summary: Lorem Ipsum
description: Lorem Ipsum
operationId: getAccountsProcessed
parameters:
- name: usertoken
in: header
description: The API token issued to authenticate and authorize the protected
endpoint
required: true
schema:
type: string
requestBody:
description: Payload with timeframes
content:
'*/*':
schema:
$ref: '#/components/schemas/AccountsProcessedInfo'
required: false
responses:
200:
description: Successfully retrieved processed account information
content:
application/json:
schema:
$ref: '#/components/schemas/Success'
400:
description: Bad request from client
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
401:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
x-swagger-router-controller: mainController
x-codegen-request-body-name: AccountsProcessedInfo
components:
schemas:
Success:
type: object
properties:
data:
type: object
properties: {}
results:
type: object
properties:
err:
type: boolean
default: false
msg:
type: string
Error:
type: object
properties:
data:
type: object
properties: {}
results:
type: object
properties:
err:
type: boolean
default: true
msg:
type: string
SaveRequest:
required:
- action
- detectionMethod
- detectionTime
- identityType
- identityValue
- realmId
type: object
properties:
identityValue:
type: string
identityType:
type: string
enum:
- A
- B
- C
realmId:
type: string
enum:
- A
- B
- C
- D
action:
type: string
enum:
- A
- B
detectionMethod:
type: string
zincId:
type: string
deferredTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
ipAddress:
type: string
userAgent:
type: string
httpMethod:
type: string
enum:
- GET
- HEAD
- POST
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
httpUrl:
type: string
detectionTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
MarkActionExecuted:
required:
- requestID
type: object
properties:
requestID:
maximum: 2147483647
minimum: 1
type: integer
format: int32
AccountsProcessedInfo:
required:
- endTime
- startTime
type: object
properties:
startTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
endTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
AccountInfo:
required:
- email
- requestId
- securityInfoSrc
type: object
properties:
requestId:
maximum: 2147483647
minimum: 1
type: integer
format: int32
baseInfoSrc:
type: string
firstName:
type: string
lastName:
type: string
omsCid:
type: integer
securityInfoSrc:
type: string
principalId:
type: string
loginUid:
type: string
realmId:
type: string
email:
type: string
format: email
example: sample@example.com
accountEnabled:
type: boolean
accountLocked:
type: boolean
failPasswordCount:
maximum: 2147483647
minimum: 0
type: integer
format: int32
lastPwdUpdateTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
creationTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
lastLoginTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
loginIdChangeTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
lastActivityTime:
maximum: 2.147483647E+12
minimum: 0
type: integer
format: int64
Forward:
required:
- fromHours
- identityValue
- realmId
type: object
properties:
identityValue:
type: string
realmId:
type: string
enum:
- A
- B
- C
- D
fromHours:
maximum: 2147483647
minimum: 0
type: integer
format: int32
userAgent:
type: string
emailDomain:
type: string
UserAuthInfo:
required:
- username
type: object
properties:
username:
type: string
Here is my server.js file:
const appConfig = require("./config");
const constants = require("./constants");
const controllers = require("./controllers/mainController");
const fs = require("fs");
const path = require("path");
const app = require("express")();
const http = require("http");
const expressOpenAPI = require("express-openapi");
const swaggerExpress = require("swagger-express-mw");
const jsyaml = require("js-yaml");
const morgan = require("morgan");
const helmet = require("helmet");
const utils = require("./helpers/utils");
const logger = require("@walmart/sts-node-module-logging").getLogger(__filename);
const secureconfig = require("@walmart/sts-node-module-secureconfig");
const permissionsMiddleware = require("@walmart/sts-node-module-auth").middleware;
const errorHandlerMiddleware = require("./middleware/validationErrorHandler");
const validationMiddleware = require("./middleware/validationMiddleware");
//CORS
app.use((req, res, next) => {
//Enabling CORS
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, contentType,Content-Type, Accept, Authorization");
next();
});
// Use helmet to provide various security measures
app.use(helmet());
// Configure logging before we get too far
logger.applyUserSettings(constants.loggingOptions);
/*
* Configure morgan for logging http requests and responses.
* Run morgan early on in the middleware stack (before apiAuth)
* so we can still log request/response details even if the
* request gets denied.
*/
app.use(morgan("combined", {
// Pipe morgan logs to our main logger
"stream": logger.streamForHttp
}));
// Add permissions middleware, since this middlware is async, we
// use the "then" callback to insert into the app as a middleware
// when the promise is resolved.
permissionsMiddleware({
audience: [ appConfig.env.authToken.accountService.audience ],
issuer: appConfig.env.authToken.issuer,
publicKey: appConfig.env.authToken.keys.public,
bypassAuth: [
"^\/info\/api\/heartbeat$",
"^\/docs.*"
],
authorize: utils.authorize
}).then((mw) => app.use(mw));
// Figure out where our controllers are. They could be in one of two locations
let controllerDir = path.join(__dirname, "controllers");
controllerDir = fs.existsSync(controllerDir) ? controllerDir : path.join(path.resolve(__dirname, ".."), "src/controllers");
// The Swagger document (require it, build it programmatically, fetch it)
const spec = fs.readFileSync(path.join(__dirname, "api/swagger/swagger.yaml"), "utf8");
const swaggerDoc = jsyaml.safeLoad(spec);
function validateAllResponses(req, res, next) {
const strictValidation = req.apiDoc["x-express-openapi-validation-strict"] ? true : false;
if (typeof res.validateResponse === "function") {
const send = res.send;
res.send = function expressOpenAPISend(...args) {
const onlyWarn = !strictValidation;
if (res.get("x-express-openapi-validation-error-for") !== undefined) {
return send.apply(res, args);
}
const body = args[0];
let validation = res.validateResponse(res.statusCode, body);
let validationMessage;
if (validation === undefined) {
validation = { message: undefined, errors: undefined };
}
if (validation.errors) {
const errorList = Array.from(validation.errors).map(_ => _.message).join(",");
validationMessage = `Invalid response for status code ${res.statusCode}: ${errorList}`;
console.warn(validationMessage);
// Set to avoid a loop, and to provide the original status code
res.set("x-express-openapi-validation-error-for", res.statusCode.toString());
}
if (onlyWarn || !validation.errors) {
return send.apply(res, args);
} else {
res.status(500);
return res.json({ error: validationMessage });
}
};
}
next();
}
expressOpenAPI.initialize({
app,
apiDoc: {
...swaggerDoc,
"x-express-openapi-additional-middleware": [ validateAllResponses ],
"x-express-openapi-validation-strict": true
},
operations: {
heartbeat: controllers.heartbeat,
saveRequest: controllers.saveRequest,
markActionExecuted: controllers.markActionExecuted,
saveAccountInfo: controllers.saveAccountInfo,
forwardRequest: controllers.forwardRequest,
getUserAuthInfo: controllers.getUserAuthInfo,
getAccountsProcessed: controllers.getAccountsProcessed
},
paths: controllerDir
});
// Handle any Swagger validation failures
app.use(errorHandlerMiddleware.errorHandler);
// Add validation for identity type and realm
app.use(validationMiddleware.validateIdentityAndRealm);
const swaggerExpressConfig = {
appRoot: __dirname
};
// Set up swagger express middleware (needed for swagger router to do its magic)
swaggerExpress.create(swaggerExpressConfig, (err, swaggerExpressApp) => {
if (err) {
throw err;
}
swaggerExpressApp.register(app);
});
// Start the server only after the secureconfig initialization is done and all the
// environment secrets are loaded.
secureconfig.initConfig().then(() => {
logger.info(`Initialized the secureconfig module`);
appConfig.loadSecrets();
// Setting up server
const server = http.createServer(app);
const port = process.env.PORT || appConfig.env.port;
server.listen(port);
logger.info(`Server started on port: ${port}; Mode: ${appConfig.envMode}`);
}).catch((err) => {
logger.fatal("Failed to start server", err);
});
Hello there! I am using the
express-openapi
module v7.2.0 on nodejs v12.18.2. The OS of the computer I am running it on is Windows Server 2019 Datacenter edition. Whenever I attempt to use an OpenAPI v3 yaml file,express-openapi
throws validation errors that shows it is validating it against the swagger v2 specification. This is my debug file: 2020-12-21T21_54_01_145Z-debug.log Here is my yaml file:Here is my server.js file:
Thanks!