I have a swagger.yaml file in nodejs application given below
/updateuser/{userId}:
x-swagger-router-controller: User
put:
tags:
- User
summary: Update User
description: Update User
operationId: updateUser
parameters:
- name: userId
in: path
description: userId for which subscription needs to be updated
type: string
required: true
- name: subData
in: body
description: Subscription To be updated
schema:
type: array
items:
$ref: "#/definitions/userDataInput"
responses:
"200":
description: Success
schema:
$ref: "#/definitions/Response"
"500":
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
definitions:
userDataInput:
required:
- productId
- subscriptionId
properties:
productId:
type: string
subscriptionId:
type: string
Now I'm validating subData in my user controller using nodejs module swagger-model-validator for example given below code
var yaml = require('js-yaml')
var fs = require('fs')
var swaggerObject
try {
swaggerObject = yaml.safeLoad(fs.readFileSync('swagger.yaml'), 'utf8')
} catch (err) {
// Error here
}
var Validator = require('swagger-model-validator')
var validator = new Validator(swaggerObject)
var validation = validator.swagger.validateModel('subData', body,
false, true)
if (validation.valid) {
// logic here
} else {
// show error
}
User will call this API like PUT /updateuser/{userId} and body parameter
subData
[{
"userId": "DSHS333FHFHD",
"productId": "465454445",
},
"userId": "RYY48433FHFHD",
"productId": "435654125",
}
]
But the problem is, it is not validating array of objects userDataInput, I have given productId and subscriptionId required field, if I omit productId it's not giving any error and also if put some extra field for ex productId1 it's not validating also. Any help.
I think the problem is in the Swagger Object you are creating. You might be better off using the validator directly and passing in your models manually.
validator.validate(object, swaggerModel, swaggerModels, allowBlankTarget, disallowExtraProperties);
You appear to be passing in the Swagger Definition and not the swagger client which generally has already decoded your definition.
The swaggerModel would be your subData schema section and the swaggerModels would be your definitions section.
Related
I am building a project in REST API. One of my models is reservation as described below.
const reservation = new mongoose.Schema({
customerName: {
type: String,
required: true,
minlength: 3,
maxlength: 50
},
date: { type: Date, required: true },
message: {
type: String,
minlength: 3,
maxlength: 250
}
})
I am building a PUT route to update the reservation (shorter version below for simplicity)
router.put('/:id', async (req, res) => {
const { customerName, message, date } = req.body;
const reservation = await Reservation.findByIdAndUpdate(req.params.id, { customerName, message, date: new Date(date) }, {
new: true
});
res.send(reservation);
});
If the client doesn't pass the req.body.message it becomes null in the database. Which makes sense. But what is the way to, instead of overwriting the message to null, just ignore it, and keep the message as it is? There has to be a better way then writing if statements and conditions.
Rule of thumb : DO NOT accept any input as it is. You should validate the payload.
The cleanest solution is to return 422 status code message if some fields are missing or not validated based on your data format.
For example; if customerName is required field and it should be in a specific format and type but client does not send this field or send the field with empty value or too long or not correct data type then return 422 http status code with error response to explain what the issue is.
I’m currently working on building end-to-end testing for an API another team is working on, and I was wondering if anyone perhaps knows about a JS library that I could use to test whether an extra field is returned in HTTP response body? The purpose of this functionality would be to keep the QA team informed when the dev team makes changes to the api via the tests, instead of the developers manually having to let us know they’ve created updates. I know this can be implemented manually but if the wheel already exists, I’d prefer to avoid recreating it lol.
Example scenario:
API call: GET user
- returns : user name, user ID and user birthday.
With proposed functionality, if the dev team made updates to the Get user call, and it returns the following
- return : user name, user ID, user birthday AND user address.
A test would fail to let me know that an extra field that wasn't expected (user address) was returned.
Schema validation seems to be what you are looking for. Besides the library mentioned in another answer, you may also want check a similar one: joi
const Joi = require('joi');
const schema = Joi.object().keys({
userName: Joi.string().alphanum().required(),
userId: Joi.number().required(),
userBirthDay: Joi.number().required(),
})
const result = Joi.validate({
userName: 'johndoe',
userId: 1234567,
userBirthDay: 1970,
userAddress: 'John Doe St.'
}, schema);
if (result.error) {
console.log(result.error.details);
}
In the spec you can make assertion on existence of error key in result object using the assertion library of your choice.
The example above assumes that you are using nodejs as an environment to run tests, but browser version of joi also exists: joi-browser
You need schema validation, there are libraries out there like ajv.
var ajv = new Ajv({ allErrors: true }); // options can be passed, e.g. {allErrors: true}
// API call: GET user - returns : user name, user ID and user birthday.
// With proposed functionality, if the dev team made updates to the Get user call, and it returns the following - return : user name, user ID, user birthday AND user address.
var schema = {
type: "object",
properties: {
userName: {
type: "string",
},
userId: {
type: "string",
},
userBirthdate: {
type: "string",
},
},
required: ["userName", "userId", "userBirthdate"],
additionalProperties: false,
};
var validate = ajv.compile(schema);
var validUser = {
userName: "John",
userId: "john",
userBirthdate: "01012000",
};
var invalidUser = {
userName: "John",
userId: "john",
userBirthdate: "01012000",
userAddress: "World",
};
var valid = validate(validUser);
console.log(`Valid user is valid: ${valid}`);
valid = validate(invalidUser);
console.log(`Invalid user is valid: ${valid}`);
console.log('Validate errors:', validate.errors);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.6.2/ajv.min.js"></script>
In a Meteor project , and I'm using [collection2 package]
I have the following collection2 Schema:
var schema = new SimpleSchema ({
comments: {
type: [{text: String, createdAt: Date}],
optional: true
}})
And when I use this query in Meteor method :
Articles.update({_id: articleId}, {$push: {comments: {text: "yryd"}}})
It insert a blank object in comments array ...
OK there is no problem in this query cause i run it in mongo terminal and all thing seems good and the insert operation done
What is the problem in your opinion?
Your schema basically appears to be incorrect for what you want to do here. It most likely needs to look something like this:
Articles new Meteor.collection("articles");
CommentSchema = new SimpleSchema({
"text": { type: String },
"createdAt": { type: Date, defaultValue: Date.now }
});
Articles.attachSchema(
new SimpleSchema({
"comments": [CommentsSchema]
})
);
Then when you add in new things your schema types are verified for the "text" field being present, and fields like "createdAt" are added to the sub-document within the array entry automatically.
I have 2 Collections of article and comment and i would like to add the articleID to the Comment Collection when creating a new comment. The same way in which the UserID automatically goes in.
comment.client.controller.js
// Create new Comment
$scope.create = function() {
// Create new Comment object
var comment = new Comments ({
details: this.details,
status: this.status,
created: this.created,
});
// Redirect after save
comment.$save(function(response) {
$location.path('comments/' + response._id);
// Clear form fields
$scope.status = '';
$scope.details = '';
$scope.created = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
comment.server.model.js
var CommentSchema = new Schema({
userName: {
type: String,
default: 'To Do',
required: 'Please fill Comment name',
trim: true
},
created: {
type: Date,
default: Date.now
},
details: {
type: String,
trim: true
}
,
user: {
type: Schema.ObjectId,
ref: 'User'
},
article: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Article'
}
});
Essentially what i need to know is where does the ObJectID for the User come from without having user : this.user or similar like you need for details, status and created. How can i get the same for Article to just automatically include the Article ObjectID?
The id will come from the route itself or if you intentionally send it inside the POST request body.
Example with article id in route:
If a user posts a comment to an article (with id 123456789) it will make a POST request to /api/articles/123456789/posts then in the backend of the app you will have the endpoint defined as follows:
app.route('/api/articles/:articleId/posts').post(article.postComment);
Note the :articleId in the route, now what you can do is bind that route param to use in your server controllers like so:
app.param('articleId', article.articleById);
articleById is the middleware that will use mongoose to find the article. articleById is defined in the article server controllers file.
Then in your postComment controller you can access the id using req.article.
If you take a look at the server routes for the articles module in meanjs, you will notice that it was already done, so you can probably use the req.article to access the article id.
I am trying to define a mongo Schema using mongoose. I need to create an 'Event Schema' in which users are referenced. So I am populating the 'users' field with the referenced ObjectId of the user Schema. However I also need to add some extra fields on that user property which are specific to the event. So something like as follows:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var EventSchema = new Schema({
name: String,
date: Date,
users: [{
profile: {
type: Schema.ObjectId,
ref: 'User'
},
RankNumber: Number,
bibNumber: Number
}],
published: Boolean
});
mongoose.model('Event', EventSchema);
However this doesn't work. I am not sure the best way to do what I am trying to achieve.
So if I have a constructor function such as:
function User(bib, rank, profile) {
this.bib = bib;
this.rank = rank;
this.profile = profile;
}
and then I call that constructor and pass in a user id as the profile property, MongoDB will create a new id field. I get a JSON response like this:
{
"name": "Event name",
"_id: "mongoid",
"users": [
{
"bibNumber": "278",
"rankNumber": "21",
"profile": "users mongo _id number",
"_id": "a new mongo _id"
}
]
}
I need to populate the profile field. But the following won't work:
Event.find().populate('users').exec(function (err, events) {....
You have to use, as I said in the comments:
Event.find(...).populate('users.profile').exec(...);