Fastify schema fails to validate type of a property of a nested object - fastify

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.

Related

How to insert post method with mongoose having a reference in node.js

I'm using mongoose and have two schema models and one of them "control" has a reference in the fist model which is "framework" model.
With node.js I'm trying to create a post method and testing it in the postman which is not successful. Not sure if I'm approaching this the right way:
Framework Schema:
const FrameworkSchema = new Schema({
name: {
type: String,
trim: true
},
slug: {
type: String,
slug: 'name',
unique: true
},
image: {
data: Buffer,
contentType: String
},
description: {
type: String,
trim: true
},
isActive: {
type: Boolean,
default: true
},
control:
{
type: Schema.Types.ObjectId,
ref: 'Control'
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
My Control schema:
const ControlSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
mControlNo: {
type: String
},
sControlNo: {
type: String
},
name: {
type: String,
trim: true
},
slug: {
type: String,
slug: 'name',
unique: true
},
description: {
type: String,
trim: true
},
isApplicable: {
type: Boolean,
default: true
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
My router api:
router.post(
'/add',
auth,
role.checkRole(role.ROLES.Admin),
async (req, res) => {
try {
const name = req.body.name;
const description = req.body.description;
const isActive = req.body.isActive;
const control = {
mControlNo: req.body.control.mControlNo,
sControlNo: req.body.control.sControlNo,
name: req.body.control.name,
description: req.body.control.description,
isApplicable: req.body.control.isApplicable
};
const framework = new Framework({
name,
description,
isActive,
control
});
const frameworkDoc = await framework.save();
res.status(200).json({
success: true,
message: `Framework has been added successfully!`,
framework: frameworkDoc
});
} catch (error) {
res.status(400).json({
error
// error: 'Your request could not be processed. Please try again.'
});
}
}
);
My json document when I tested it in postman:
{
"name": "NCA2",
"description": "testdescription",
"isActive": true,
"control":
{
"mControlNo": "1",
"sControlNo": "2",
"name": "controltest",
"description": "controldescription",
"isApplicable": true
}
}
Response I'm getting:
{
"error": {
"errors": {
"control": {
"stringValue": "\"{\n mControlNo: '1',\n sControlNo: '2',\n name: 'controltest',\n description: 'controldescription',\n isApplicable: true\n}\"",
"kind": "ObjectId",
"value": {
"mControlNo": "1",
"sControlNo": "2",
"name": "controltest",
"description": "controldescription",
"isApplicable": true
},
"path": "control",
"reason": {}
}
},
"_message": "Framework validation failed",
"message": "Framework validation failed: control: Cast to ObjectId failed for value \"{\n mControlNo: '1',\n sControlNo: '2',\n name: 'controltest',\n description: 'controldescription',\n isApplicable: true\n}\" at path \"control\""
}
}
The whole point of using a framework like mongoose is to write models and let the framework check for you if the body you're sending it is right or wrong. You don't have to assign each variable from your body to your model. You can simply write the following, which will save lines:
const framework = new Framework(req.body);
(Of course, granted that the body has been correctly parsed to JSON via body-parser or another parser).
Then, you check for description or name to be present:
if (!description || !name)
but none of them exist. req.body.description and req.body.name do exist, possibly framework.description and framework.name do as well, but description or name are undefined variables.
The rest of the code looks good, if the error persists, please print out the error in the catch clause as others have suggested in the comments.
Following the code added in the question and the comments the OP made, we now have more elements to answer.
You have a ValidationError coming from mongoose, meaning one of the field you entered is not right. You can also see that it's coming from the field control.
In your Framework schema, you declare a field control that looks like that:
control:
{
type: Schema.Types.ObjectId,
ref: 'Control'
},
That means that Framework is taking an ObjectID for this field, and not an object like you send it here:
const framework = new Framework({
name,
description,
isActive,
control
});
The error is explicit on its own: Mongoose tried to cast your control object to an ObjectID and of course, it fails.
You have 2 solutions:
Either you implement your Control schema directly in your Framework schema as an object field
Or you create a separate Schema object in your route, save it, and give the ID to control when creating the Framework object.
The second solution could look something like this:
const control = new Control(req.body.control);
const controlDoc = await control.save();
const framework = new Framework({...req.body, control: controlDoc._id});
const frameworkDoc = await framework.save();
The first could look something like this:
const FrameworkSchema = new Schema({
// all your Framework schema
control:
{
mControlNo: { type: String },
sControlNo: { type: String },
name: { type: String, trim: true},
// and so on....
},
});

How to make Swagger UI accepts request body with optional field

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/

Fastify Route Params required is not honored

I have a route something like this:
// collection GET
fastify.route({
method: 'GET',
url: `${constants.EXTERNAL_PATH}/:serviceName/:collectionName/account/:accountId`,
schema: {
description: 'Get all documents from the specified table for the specified service database and account.',
summary: 'Get all documents from the specified table.',
tags: ['persistence'],
headers: {
$ref: 'authorization-user#'
},
params:{
type: 'object',
properties: {
serviceName: { type: 'string' },
collectionName: { type: 'string' },
accountId: { type: 'string' }
},
required: ['serviceName', 'collectionName', 'accountId'],
},
response: {
200: {
properties: {
'documents': {
description: 'All the retrieved documents from the specified table for the specified service database and account.',
type: 'array',
items: {
$ref: 'persistence-response-doc#',
}
},
},
},
'4xx': {
$ref: 'error-response#',
description: 'Error response'
}
}
},
handler: async (request, reply) => {
// Logic goes here //
}
});
Now my expectation was that:
This should work:
http://localhost:9011/persistence/external/myservice/mycollection/account/my_actn_id
As it has the service as myservice, collection as mycollection and accountId as my_actn_id
However this should fail with a route error (say when I am missing the :accountId, which is required):
http://localhost:9011/persistence/external/myservice/mycollection/account/ <== should fail
However both are passing, returning same collection results.
What am I missing here?
Thanks,
Pradip

Loopback 4 How to validate requestBody properties

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"
}
}
]
}
}

NodeJS JSON Schema Validation not working

I'm a complete newbie to JSON Schema Validator, but I think is very powerful. However, I just can't validate one JSON.
This is my Schema
{
title: "Example Schema",
type: "object",
properties: {
original_image:{
type: "object",
properties: {
temp_id: {type: "string"},
url: {type: "string"},
scale:{
type: "object",
properties:{
new_width: {type: "number"},
new_height: {type: "number"}
},
required:["new_width","new_height"]
}
},
required:["url","temp_id","scale"]
}
},
required:["image"]
}
And this is the actual JSON:
{
"original_image": {
"temp_id": "this is my id",
"scale": {
"new_width": null,
"new_height": 329
}
}
}
So as you can see the "url" property from "original_image" is not there, but the validation returns true! And, for "new_width" I set the value to null... and again passes validation, so I don't know what I'm doing wrong.
It seems to be working fine. The console logs errors correctly. This is my index.js
var Validator = require('jsonschema').Validator;
var v = new Validator();
var instance = {
"original_image": {
"temp_id": "this is my id",
"scale": {
"new_width": null,
"new_height": 329
}
}
};
var schema = {
title: "Example Schema",
type: "object",
properties: {
original_image:{
type: "object",
properties: {
temp_id: {type: "string"},
url: {type: "string"},
scale:{
type: "object",
properties:{
new_width: {type: "number"},
new_height: {type: "number"}
},
required:["new_width","new_height"]
}
},
required:["url","temp_id","scale"]
}
},
required:["image"]
};
console.log(v.validate(instance, schema));
If you put your condition as required:["url","temp_id","scale"] , then, all three properties are required in the payload, but url seems to be missing in your payload.
If you want url to be optional, then, dont put it in the required constraint.
The validator also gives back an error message.It returns missing parameters/properties if that is the case.

Resources