OpenAPI: "request should have required property 'body'" - node.js

I am building out a new endpoint in my application which uses express-openapi-validator as validator middleware.
/* index.ts */
import * as OpenApiValidator from 'express-openapi-validator';
const whitelistedPaths = [/* regex tested paths */];
app.use(
OpenApiValidator.middleware({
apiSpec: './schema/api.json',
validateResponses: true,
ignorePaths: whitelistedPaths,
validateSecurity: true,
}),
);
/* ... */
app.post(
'/users/:email/validateToken',
bodyParser.json(),
(req) => validateToken(req.params.email, req.body.resetToken),
);
In my configuration (api.json) file I've defined the schema for my endpoint as:
"/users/{email}/validateToken": {
"post": {
"tags": ["users"],
"summary": "Validate user token",
"operationId": "validateToken",
"responses": {
"200": {
"description": "Ok",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"parameters": [
{
"name": "email",
"in": "path",
"description": "User email",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["resetToken"],
"properties": {
"resetToken": {
"type": "string"
}
}
}
}
}
}
}
},
I've tested with Postman with the following JSON body:
{
"resetToken": "randomd9320ru9"
}
but receive the following error message:
{
"message": "request should have required property 'body'",
"errors": [
{
"path": ".body",
"message": "should have required property 'body'",
"errorCode": "required.openapi.validation"
}
]
}
I'm not sure why it's complaining about the body. I tried putting "required": true under the requestBody config in api.json but that didn't change anything. I just want to make sure that the body includes the required field resetToken.

I suppose you need to use bodyParser.json() before using OpenApiValidator.middleware:
app.use(bodyParser.json());
app.use(
OpenApiValidator.middleware({
apiSpec: './schema/api.json',
validateRequests: true,
validateResponses: true,
ignorePaths: whitelistedPaths,
validateSecurity: true,
}),
);
...
app.post(
'/users/:email/validateToken',
(req) => validateToken(req.params.email, req.body.resetToken),
);

Related

Can I define an extend field for response component Swagger?

Current, I use #/components/responses/Default response as reuseable properties for all api defines. But some API need to add more a field but not change default response format. So Can I reUse default response and add more field for response
{
"openapi": "3.0.3",
"path": {
"/login": {
"post": {
"responses": {
200: {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/responses/defaultResponse"
}
}
}
}
}
}
}
},
"components": {
"schemas" :{
"responseSchema": {
"type": "object",
"properties": {
"httpCode": { "type": "integer" },
"message": { "type": "string" }
}
}
},
"responses": {
"defaultResponse": { "$ref": "#/components/schemas/responseSchema" }
}
}
}
Above is my swagger spec. but with Login, if success I want to put more a field (token) to return token for client, Can I do it with this or have to manual define schema ?
In OpenAPI version 3, you do this with the allOf keyword. Detail document
{
"openapi": "3.0.3",
"info": {
"title": "Example",
"version": "1.0"
},
"paths": {
"/login": {
"post": {
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/responses/defaultResponse"
},
{
"type": "object",
"properties": {
"token": {
"type": "string"
}
}
}
]
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"responseSchema": {
"type": "object",
"properties": {
"httpCode": {
"type": "integer"
},
"message": {
"type": "string"
}
}
}
},
"responses": {
"defaultResponse": {
"$ref": "#/components/schemas/responseSchema"
}
}
}
}

Sending array of objects in form-data. Swagger, OpenAPI 3

I am trying to send a form-data request which has an array of objects. The problem is that the data that I receive on my Express server comes in the form of an array in which all objects are turned into a string. I can't change anything in the server, I need to solve this problem using Swagger.
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"video[]": {
"type": "array",
"items": {
"type": "object",
"properties": {
"_id": {
"type": "string"
}
}
},
"describtion": "Video ids "
}
}
},
"encoding": {
"video[]": {
"contentType": "application/json",
"explode": true
}
}
}
}
},
What I expect on server: { video: [{ _id: "string" }] }
What I get: { video: [ '{"_id": "string"}' ] }
it seems you are not parsing the 'video' property. Try the below code in the controller function.
const {video} = req.body;
parsedVideo = JSON.parse(video);
console.log(parsedVideo);

Joi.forbidden() equivalent in Mongoose

The below schema allows me to have the optionalField required if value is set to option1 but if value is set to option2 then optionalField can be set to anything the user desires. Instead, I want the validation to fail if value is set to anything other than option1 and optionalField is passed in.
const validValues = ['option1', 'option2']
const sampleSchema = new mongoose.Schema({
value: {
type: String,
enum: validValues,
required: true
}
optionalField: {
type: Number,
required: function() { return this.value === 'option1' }
// validation should fail if this property is passed in when value is anything but option1
}
})
Joi has an excellent way to achieve this using Joi.forbidden():
const validValues = ['option1', 'option2']
const schema = Joi.object({
value: Joi.string().valid(...validValues).required(),
optionalField: Joi.string().when('value', { is: 'option1', then: Joi.required() otherwise: Joi.forbidden() })
})
If I validate with this schema and optionalField is passed in to Joi, the validation will fail unless value is option1.
I am hoping to find a way to achieve the same in Mongoose.
Thank You!
You can use custom validators like this:
optionalField: {
type: Number,
required: function() {
return this.value === "option1";
},
validate: {
validator: function(v) {
console.log({ v });
return !(this.value !== "option1" && v.toString());
},
message: props =>
`${props.value} optionalField is forbidden when value is not option1`
}
}
Sample route:
router.post("/sample", (req, res) => {
const sample = new Sample(req.body);
sample
.save()
.then(doc => res.send(doc))
.catch(err => res.status(500).send(err));
});
Input 1:
{
"value": "option2",
"optionalField": 11
}
Result 1: (error)
{
"errors": {
"optionalField": {
"message": "11 optionalField is forbidden when value is not option1",
"name": "ValidatorError",
"properties": {
"message": "11 optionalField is forbidden when value is not option1",
"type": "user defined",
"path": "optionalField",
"value": 11
},
"kind": "user defined",
"path": "optionalField",
"value": 11
}
},
"_message": "Sample validation failed",
"message": "Sample validation failed: optionalField: 11 optionalField is forbidden when value is not option1",
"name": "ValidationError"
}
Input 2:
{
"value": "option2"
}
Result 2: (success)
{
"_id": "5e031b473cbc432dfc03fa0e",
"value": "option2",
"__v": 0
}
Input 3:
{
"value": "option1"
}
Result 3: (error)
{
"errors": {
"optionalField": {
"message": "Path `optionalField` is required.",
"name": "ValidatorError",
"properties": {
"message": "Path `optionalField` is required.",
"type": "required",
"path": "optionalField"
},
"kind": "required",
"path": "optionalField"
}
},
"_message": "Sample validation failed",
"message": "Sample validation failed: optionalField: Path `optionalField` is required.",
"name": "ValidationError"
}
Input 4:
{
"value": "option1",
"optionalField": 11
}
Result 4: (success)
{
"_id": "5e031ba83cbc432dfc03fa10",
"value": "option1",
"optionalField": 11,
"__v": 0
}

How to represent custom token in header in Swagger UI(swagger.json) in nodejs

I am creating a Restful server in ExpressJs. I have integrated swagger-jsdoc. Following is the related files. Below(header.png) is how I expect my header to look like in swagger UI. But, while I open my swagger UI (http://localhost:3000/api-docs/), I am not able to see Token tags (token and Authentication) in the header.
swagger.json
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Viswa API"
},
"host": "localhost:3000",
"basePath": "/api",
"tags": [{
"name": "Customers",
"description": "API for customers in the system"
}],
"schemes": [
"http"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"securityDefinitions": {
"Bearer": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
},
"JWT": {
"type": "apiKey",
"name": "token",
"in": "header"
}
},
"paths": {
"/customer": {
"post": {
"tags": [
"Customers"
],
"description": "Create new customer in system",
"parameters": [{
"name": "customer",
"in": "body",
"description": "Customer that we want to create",
"schema": {
"$ref": "#/definitions/Customer"
}
}],
"produces": [
"application/json"
],
"responses": {
"201": {
"description": "New customer is created",
"schema": {
"$ref": "#/definitions/Customer"
}
}
}
}
}
},
"definitions": {
"Customer": {
"required": [
"email"
],
"properties": {
"customer_name": {
"type": "string"
},
"customer_email": {
"type": "string"
}
}
}
}
}
app.route
var apiRoutes = express.Router();
app.use('/api', apiRoutes);
// swagger definition
var swaggerUi = require('swagger-ui-express'),
swaggerDocument = require('../swagger.json');
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.use('/api/v1', apiRoutes);
Current Swagger UI:
You are missing the security tag. You can define it either globally just below the securityDefinitions tag or one for each API endpoint.
Have a look at this question.
You can add a security tag
"security": [ { "Bearer": [] } ],
After adding it your path section
"paths": {
"/customer": {
"post": {
"security": [ { "Bearer": [] } ],
"tags": [
"Customers"
],
"description": "Create new customer in system",
"parameters": [{
"name": "customer",
"in": "body",
"description": "Customer that we want to create",
"schema": {
"$ref": "#/definitions/Customer"
}
}],
"produces": [
"application/json"
],
"responses": {
"201": {
"description": "New customer is created",
"schema": {
"$ref": "#/definitions/Customer"
}
}
}
}
}
},
You can define the header parameters in the path definition. like this
"paths": {
"/customer": {
parameters: [{ name: "Authorization", in: "header", type: "string", description: "auth token" }]
}
}

Dialogflow detect intent "payload" not working

I want to send custom parameters to the webhook. According to the documentation I set it under "payload" parameter. But I don't see values I set on response object.
https://dialogflow.com/docs/reference/api-v2/rpc/google.cloud.dialogflow.v2#google.cloud.dialogflow.v2.QueryParameters
Here is my code
function detectIntent(query, sessionId, contextData, callback) {
let projectId = config.get('projectId');
let languageCode = 'en-US';
let sessionPath = sessionClient.sessionPath(projectId, sessionId);
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
languageCode: languageCode,
}
},
queryParams: {
contexts: [
contextData
],
payload: { foo: "bar" }
},
};
sessionClient
.detectIntent(request)
.then(responses => {
const result = responses[0].queryResult;
callback(null, result);
})
.catch(err => {
callback(err, null);
});
}
Here is the response I'm getting
{
"fulfillmentMessages": [
{
"platform": "PLATFORM_UNSPECIFIED",
"text": {
"text": [
""
]
},
"message": "text"
}
],
"outputContexts": [
{
"name": "projects/ddddd-102d1/agent/sessions/blvy6skjngu4kvt/contexts/blvy6skjngu4kvu",
"lifespanCount": 2,
"parameters": {
"fields": {
"msisdn": {
"stringValue": "773959698",
"kind": "stringValue"
}
}
}
},
{
"name": "projects/ddddd-102d1/agent/sessions/blvy6skjngu4kvt/contexts/actionshow_card-followup",
"lifespanCount": 2,
"parameters": null
}
],
"queryText": "internet slow",
"speechRecognitionConfidence": 0,
"action": "action.show_card",
"parameters": {
"fields": {}
},
"allRequiredParamsPresent": true,
"fulfillmentText": "",
"webhookSource": "",
"webhookPayload": null,
"intent": {
"inputContextNames": [],
"events": [],
"trainingPhrases": [],
"outputContexts": [],
"parameters": [],
"messages": [],
"defaultResponsePlatforms": [],
"followupIntentInfo": [],
"name": "projects/ddddd-102d1/agent/intents/e378b17a-d899-4e69-8dfd-4b938b0222a5",
"displayName": "action.show_card",
"priority": 0,
"isFallback": false,
"webhookState": "WEBHOOK_STATE_UNSPECIFIED",
"action": "",
"resetContexts": false,
"rootFollowupIntentName": "",
"parentFollowupIntentName": "",
"mlDisabled": false
},
"intentDetectionConfidence": 1,
"diagnosticInfo": {
"fields": {
"webhook_latency_ms": {
"numberValue": 161,
"kind": "numberValue"
}
}
},
"languageCode": "en-us"
}
I tried with formatting the payload json by doing structjson.jsonToStructProto({foo: 'bar'}) as mention in following link
Send parameters to webhook on dialogflow sdk v2
but no success.

Resources