Well, the title is very easy to understande, but to make things detailed:
I have an expressjs application, written with TypeScript. The app receives a request, where the JSON body is like this:
{
name: "name",
description: "description",
github: "github",
logo: "logo",
app: "app"
}
But the thing is: The app property is optional, since I will insert the object in the database WITH that property only if the request was made with it declared.
I'd like to know if it's possible to make the Swagger accept that optional thing, and if so, how to do that?
EDIT:
As requested by #Anatoly, this is the swagger route definition:
post: {
summary: 'Create a new project',
tags: ['projects'],
requestBody: {
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Project'
}
}
}
}
},
components: {
schemas: {
Project: {
type: 'object',
properties: {
name: {
type: 'string'
},
description: {
type: 'string'
},
github: {
type: 'string'
},
logo: {
type: 'string'
},
app: {
type: 'string'
}
}
}
}
}
All params except path ones are optional unless they have the required attribute as true
app:
type:string
required:true
Refer the heading Required and Optional Parameters at https://swagger.io/docs/specification/describing-parameters/
I am new in using Fastify Js. I have this route that fails to validate the type of properties inside the nested object from inside the body schema.
fastify.put('/comment', {
schema: {
body: {
type: 'object',
required: ['from', 'body', 'courseCode'],
properties: {
from: {
type: 'object',
required: ['email', 'username'],
properties: {
// this is the part where validation fails
email: { type: 'string' },
username: { type: 'string' },
}
},
body: { type: 'string' },
courseCode: { type: 'string' },
}
}
}
}, async (req, rep) => {
// logic
return {someResponse};
});
I am using REST Client extension in VSCode. This is my sample request
put http://localhost:3000/comment
Content-Type: application/json
{
"from": {
/* Notice the `email` and `username`, Fastify does not throw an error or some exception
that tells this is suppose to be a string and not an integer */
"email": 3123,
"username": 123123
},
"body": "some comment from enginex",
"courseCode": "d-c"
}
I also try using insomia.io and postman but the result is the same.
Did I miss something important here? Thank You so much.
I am following this toutorial: https://github.com/codeBelt/open-api-documentation/blob/master/src/openApiDocumentation.js
Can I validate my openApiDocumentation.js file somewhow? I get:
Unable to render this definition
The provided definition does not specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: "2.0" and those that match openapi: 3.0.n (for example, openapi: 3.0.0).
I am attaching mi .js file. Maybe you guys will see a typo here. Thanks in advance.
.js file:
const USER_TYPES = {
EXCHANGE: 'xxx',
GIVEAWAY: 'xxx'
}
const openApiDocumentation = {
openapi: '3.0.1',
info: {
version: '1.3.0',
title: 'xxx',
description: 'xxx',
contact: {
name: 'xxx',
email: 'xxx',
}
},
license: {
name: 'Apache 2.0',
url: 'https://www.apache.org/licenses/LICENSE-2.0.html',
},
servers: [
{
url: 'http://localhost:4000/',
description: 'Local server',
},
],
tags: [
{
name: 'Books CRUD operations',
},
],
paths: {
'/findAllBooks': {
get: {
tags: ['CRUD operations'],
description: 'Get all Book offers',
operationId: 'getUsers',
parameters: [
{
name: 'page',
in: 'query',
schema: {
type: 'integer',
default: 1,
},
required: true,
description: 'Page numer used pagination.',
},
],
responses: {
'200': {
description: 'Books were obtained',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Books',
},
},
},
},
'500': {
description: 'Missing parameters',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error',
},
example: {
message: 'page qyery parameter is missing',
internal_code: 'missing_parameters',
},
},
},
},
},
},
},
},
components: {
schemas: {
coverImg: {
type: 'string',
example: 'http:',
},
image: {
type: 'string',
example: 'http',
},
category: {
type: 'string',
example: 'Crafts & Hobbies',
},
linkTypes: {
type: 'object',
properties: {
coverImg: {
$ref: '#/components/schemas/coverImg',
},
images: {
type: 'array',
items: {
$ref: '#/components/schemas/image',
},
}
}
},
offerID: {
type: 'string',
example: '27301927',
},
userID: {
type: 'string',
example: 'efdc5192',
},
title: {
type: 'string',
example: 'Quilting For Dummies',
},
description: {
type: 'string',
example: 'You ',
},
categories: {
type: 'array',
items: {
$ref: '#/components/schemas/category',
},
},
links: {
type: 'object',
items: {
$ref: '#/components/schemas/linkTypes',
},
},
offerType: {
type: 'string',
enum: USER_TYPES,
default: USER_TYPES.EXCHANGE,
},
Book: {
type: 'object',
properties: {
offerID: {
$ref: '#/components/schemas/offerID',
},
userID: {
$ref: '#/components/schemas/userID',
},
title: {
$ref: '#/components/schemas/title',
},
description: {
$ref: '#/components/schemas/description',
},
categories: {
$ref: '#/components/schemas/categories',
},
imageLinks: {
$ref: '#/components/schemas/links',
},
offerType: {
$ref: '#/components/schemas/offerType',
},
},
},
Books: {
type: 'object',
properties: {
users: {
type: 'array',
items: {
$ref: '#/components/schemas/Book',
},
},
},
},
Error: {
type: 'object',
properties: {
message: {
type: 'string',
},
internal_code: {
type: 'string',
},
},
},
},
},
};
There are few mistakes,
license must be inside the info object
info: {
version: '1.3.0',
title: 'xxx',
description: 'xxx',
contact: {
name: 'xxx',
email: 'xxx' // make sure you have used valid email address!
},
license: {
name: 'Apache 2.0',
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
}
}
enum will allow only array not an object and here you have passed object USER_TYPES, corrected below:
const USER_TYPES = {
EXCHANGE: 'xxx',
GIVEAWAY: 'xxx'
};
const USER_TYPES_ENUM = [
USER_TYPES.EXCHANGE,
USER_TYPES.GIVEAWAY
];
offerType: {
type: 'string',
enum: USER_TYPES_ENUM,
default: USER_TYPES.EXCHANGE,
},
For best practice use https://editor.swagger.io/ (also they have provided a option to convert json to yaml under Edit > Convert to YAML)!
I'm trying make a swagger file for an api GET request that has several paths to it. There's the GET /listings, GET /listings that passes in req.query.bidderId and another one that passes req.query.watcherId and one for req.query.winnerId. Currently swagger is saying
Additional properties not allowed: /listings/{winnerId},/listings/{watcherId}
I'm not really sure how to handle all the different scenarios for one API call in swagger file?
Do I only need to have one with /{id}: and just let the express code do the rest? Is it that simple?
app.js
//GETS AUCTION LISTINGS
app.get("/api/listings", (req, res, next) => {
query = {};
if (req.query.bidderId && req.query.winner === "false") {
query = { bidderId: req.query.bidderId };
biddingGroupQuery = { biddingGroup: req.query.biddingGroup }
Post.find({
$and: [
{ biddingGroup: { $in: [req.query.bidderId] } },
{ auctionEndDateTime: { $gte: Date.now() } }
]
})
.populate("creator", "username")
.then(documents => {
req.params.Id = mongoose.Types.ObjectId(req.params.Id);
res.status(200).json({
message: "Active Bids Listings retrieved successfully!",
posts: documents
});
});
} else if (req.query.bidderId && req.query.winner) {
query = { bidderId: req.query.bidderId };
biddingGroupQuery = { biddingGroup: req.query.biddingGroup };
Post.find({
$and: [
{ bidderId: req.query.bidderId },
{ auctionEndDateTime: { $lte: Date.now() } }
]
})
.populate("creator", "username")
.then(documents => {
req.params.Id = mongoose.Types.ObjectId(req.params.Id);
res.status(200).json({
message: "Auction listings retrieved successfully!",
posts: documents
});
});
} else if (req.query.watcherId) {
Post.find({
$and: [
{ watchingGroup: { $in: [req.query.watcherId] } },
{ auctionEndDateTime: { $gte: Date.now() } }
]
})
.populate("creator", "username")
.then(documents => {
res.status(200).json({
message: "User's watchlist items retrieved!",
posts: documents
});
});
} else if (!req.query.bidderId) {
Post.find({ auctionEndDateTime: { $gte: Date.now() } })
.populate("creator", "username")
//.populate('watchlist', 'watchItemUniqueKey')
.then(documents => {
req.params.Id = mongoose.Types.ObjectId(req.params.Id);
res.status(200).json({
message: "Auction listings retrieved successfully!",
posts: documents
});
});
}
});
swagger file
swagger: "2.0"
info:
version: "1.0.0"
title: Hello World App during dev, should point to your local machine
basePath: /v1
schemes:
# tip: remove http to make production-grade
- http
- https
paths:
/listings:
x-swagger-router-controller: listings
get:
description: Return all the auction listings
operationId: getAuctions
responses:
"200":
description: Success got all the listings
schema:
$ref: "/definitions/Listing"
"500":
description: Unexpected Error
schema:
type: object
properties:
message:
type: string
/listings/{id}:
x-swagger-router-controller: listings
get:
description: My Bids, My Wins, and My Watchlist GET request
operationId: getId
parameters:
- in: path
name: id
required: true
type: string
minimum: 5
description: BidderId, WatcherId, or WinnerId
responses:
"200":
description: Success got all the listings
schema:
$ref: "/definitions/Listing"
"500":
description: Unexpected Error
schema:
type: object
properties:
message:
type: string
definitions:
Listing:
properties:
id:
type: integer
glassTitle:
type: boolean
startingBid:
type: string
increments:
type: string
shippingCost:
type: string
auctionType:
type: string
buyItNow:
type: string
snipingRules:
type: string
creator:
type: string
username:
type: string
biddingGroup:
type: array
items:
type: string
watchingGroup:
type: array
items:
type: string
lastBidderName:
type: string
currentBid:
type: string
I'm looking for a way to prevent unwanted properties to be present in the requestBody as described in the associated Model
Here is my model :
import { Model, model, property } from '#loopback/repository';
#model({
name: 'AwsS3',
strict: true,
description: 'AWS S3 Object description',
properties: {
Key: {
type: 'String',
required: 'true',
},
Bucket: {
type: 'String',
requied: 'true',
},
},
})
export class AwsS3 extends Model {
#property({
type: 'string',
description: 'path/to/file',
required: true,
}) Key: string;
#property({
type: 'string',
description: 'AWS-S3-Bucket-Name',
required: true,
})
Bucket: string;
constructor(data: AwsS3) {
super(data);
}
}
I used it like this in the controller
function(#requestBody({
required: true,
description: 'aws object settings',
content: {
'application/json': {},
},
}) body : AwsS3
){
console.log(body);
}
It throws correctly when one of both properties is missing or in the wrong type.
But if i send a json like bellow nothing is thrown and object is processed with the UnwantedProp
{
Key: 'key',
Bucket : 'bucket',
UnwantedProp: 40
}
I found it to be achievable by using the #api decorator and setting the additionalProperties: false from the openapi specs.
use it like :
#api(
basePath: '/',
paths : {
'somepath': {
'post' : {
'x-operation-name': 'myfunction',
'x-controller-name': 'MyController',
// properties for route
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
additionalProperties: false, // <=== here it is
properties: {
Key: { type: 'string'},
Bucket: {type: 'string'},
},
required: ['Bucket', 'Key'],
},
},
},
},
}
}
}
)
export class MyController{
async myfunction(
#requestBody({ settings: { strict: true } }) body
){}
}
when testing it throws the following as expected :
{
"error": {
"statusCode": 422,
"name": "UnprocessableEntityError",
"message": "The request body is invalid. See error object `details` property for more info.",
"code": "VALIDATION_FAILED",
"details": [
{
"path": "",
"code": "additionalProperties",
"message": "should NOT have additional properties",
"info": {
"additionalProperty": "unwantedProp"
}
}
]
}
}