Meteor - node simple schema validate data to match schema - rest

I want to change my Rest-API validation to node simple schema for schema definition and collection2#core for schema validation.
I want to use the Person schema to validate the data provided by the users.
Schemas = {};
Schemas.Person = new SimpleSchema({
name: {
type: String,
label: "Person's Name",
unique: true,
max: 200
},
surname: {
type: String,
unique: true,
label: "person's surname"
},
};
validData = API.utility.validate(data, Schemas.Person });
API: {
utility: {
validate: function(data, schema) {
return "The SimpleSchema Validation";
}
}
};

This case is described in the simpl-schema documentation
With your schema definition you can just do:
Schemas.person.validate(data);
If right after that you want to look at the result or the errors:
Schemas.person.isValid();
Schemas.person.validationErrors();

Related

How to validate for ObjectID

Using Joi schema validation, is it possible to validate against MongoDB ObjectID's?
Something like this could be great:
_id: Joi.ObjectId().required().error(errorParser),
const Joi = require('#hapi/joi')
Joi.objectId = require('joi-objectid')(Joi)
const schema = Joi.object({
id: Joi.objectId(),
name: Joi.string().max(100),
date: Joi.date()
})
checkout https://www.npmjs.com/package/joi-objectid
I find that if I do
Joi.object({
id: Joi.string().hex().length(24)
})
it works without installing any external library or using RegEx
The hex makes sure the string contains only hexadecimal characters and the length makes sure that it is a string of exactly 24 characters
This package works if you are using the new version of Joi.
const Joi = require('joi-oid')
const schema = Joi.object({
id: Joi.objectId(),
name: Joi.string(),
age: Joi.number().min(18),
})
package: https://www.npmjs.com/package/joi-oid
If you want a TypeScript version of the above library integrated with Express without installing anything:
import Joi from '#hapi/joi';
import { createValidator } from 'express-joi-validation';
export const JoiObjectId = (message = 'valid id') => Joi.string().regex(/^[0-9a-fA-F]{24}$/, message)
const validator = createValidator({
passError: true,
});
const params = Joi.object({
id: JoiObjectId().required(),
});
router.get<{ id: string }>(
'/:id',
validator.params(params),
(req, res, next) => {
const { id } = req.params; // id has string type
....
}
);
I share mine
let id =
Joi.string()
.regex(/^[0-9a-fA-F]{24}$/)
.message('must be an oid')
With joi naked package you can use this:
const ObjectId = joi.object({
id: joi.binary().length(12),
})
This is a core function without any external package. I have used Joi and mongoose packages to achieve it.
const Joi = require("joi");
const ObjectID = require("mongodb").ObjectID;
const schema = Joi.object({
id: Joi.string().custom((value, helpers) => {
const filtered = ObjectID.isValid(value)
return !filtered ? helpers.error("any.invalid") : value;
},
"invalid objectId", ).required(),
name: Joi.string(),
age: Joi.number().min(18),
})
I see many correct answers here, but I also want to express my opinion.
I am not against installing npm packages, but installing one package just to validate an object id is overkill. I know programmers are lazy but C'mooon :)
Here is my full code function that validates two object id properties, very simple:
function validateRental(rental) {
const schema = Joi.object({
movieId: Joi.string()
.required()
.regex(/^[0-9a-fA-F]{24}$/, 'object Id'),
customerId: Joi.string()
.required()
.regex(/^[0-9a-fA-F]{24}$/, 'object Id'),
})
const { error } = schema.validate(rental)
return {
valid: error == null,
message: error ? error.details[0].message : null,
}
}
This way, if any of the properties contain invalid id like this:
{
"movieId": "123456",
"customerId": "63edf556f383d108d54a68a0"
}
This will be the error message:
`"movieId" with value "123456" fails to match the object Id pattern`

Loopback 4/MongoDB - Foreign key not converted to ObjectID

I am trying to set up an hasMany relation using a Mongo database.
I have followed the guide to create an hasMany relation in the loopback 4 documentation (https://loopback.io/doc/en/lb4/HasMany-relation.html) and tryied to set differents properties but the foreign key custId is saved as a string and not as an ObjectID.
I also found a few other properties or options from others topics but people were using Loopback 3 and it doesn't seem to work with Loopback 4.
Did I miss something or is there any workaround ?
Here are my models :
#model()
export class Order extends Entity {
#property({
type: 'string',
id: true,
generated: true,
})
id: string;
#property({
type: 'array',
itemType: 'string',
required: true,
})
product: string[];
#property({
type: 'number',
required: true,
})
price: number;
#property({
type: 'string',
id: true,
generated: true,
})
custId: string;
constructor(data?: Partial<Order>) {
super(data);
}
}
#model()
export class Customer extends Entity {
#property({
type: 'string',
id: true,
generated: true,
})
id: string;
#property({
type: 'string',
required: true,
})
name: string;
#property({
type: 'string',
})
adress?: string;
#hasMany(() => Order, {keyTo: 'custId'})
orders?: Order[];
constructor(data?: Partial<Customer>) {
super(data);
}
}
This is currently a bug. The hasMany / belongsTo will end up saving the relation id as a string instead of an ObjectId. You can verify this by changing the id in the database directly to an ObjectId and then it will find it.
Reference: https://github.com/strongloop/loopback-next/issues/2085
It is also mentioned on the latest Monthly Milestone here, so hopefully it will be resolved soon: https://github.com/strongloop/loopback-next/issues/2313
Edit: I was able to get it working by adding strictObjectIDCoercion to the model, but that can break other things according to issue 2085 linked above.
#model({
settings: {
strictObjectIDCoercion: true,
}
})
For hasMany relationship you need to update order Model.
Update order.model with :
1.Import Customer Model
import {Customer} from './customer.model';
remove custId: string;
2.For reference customer Id Just update code with
#belongsTo(() => Customer)
custId: number;
Reference Example : here

ER_TOO_LONG_KEY in SailsJS 1.0 with sails-mysql

I got this error with sails when I try to sails lift:
info: ·• Auto-migrating... (drop)
error: A hook (`orm`) failed to load!
error:
error: Error: ER_TOO_LONG_KEY: Specified key was too long; max key length is 767 bytes
I just have one model for now: 
module.exports = {
datastore: 'default',
tableName: 'sci_user',
attributes: {
email: {
type: 'string',
required: true,
unique: true
},
password: {
type: 'string',
required: true
}
}
It's really simple and I got it from the documentation. I don't understand. It seems it's because of the unique: true.
This is due to a combination of factors, but the most pertinent one is that sails-mysql currently defaults to using the utf8mb4 character set for string attributes, to allow the use of emojis and other extended characters. We're working on a patch to make this configurable rather than the default, but in the meantime the quickest workaround is to declare the columnType for your attributes directly:
module.exports = {
datastore: 'default',
tableName: 'sci_user',
attributes: {
email: {
type: 'string',
required: true,
unique: true,
columnType: 'varchar'
},
password: {
type: 'string',
required: true,
columnType: 'varchar'
}
}

Sails.JS association validation

I'm using several one to many associations in Sails.JS that look like the following:
User
email: {
type: 'string',
required: true,
unique: true
},
projects: {
collection: 'project',
via: 'user'
}
Project
name: {
type: 'string',
required: true,
minLength: 3,
maxLength: 50
},
user: {
model: 'user',
required: true
},
sites: {
collection: 'site',
via: 'project'
}
Site
project: {
model: 'project',
required: true
},
name: {
type: 'string',
required: true
}
Now when I fire off a POST request to /project it creates the project fine, and specifying the param 'user' (taken from the session) associates the project with that particular user.
The same goes for when I create a new site. However, I appear to be able to specify any number for the param 'project', even if that particular project ID doesn't exist. Really it should fail the validation if the project doesn't exist and not create the site. I thought it'd look up the association with project and check that the project ID specified is valid?
Also, I only want to be able to create a site that is associated with a project that belongs to the current user. How would I go about doing this?
Thanks in advance.
I'm not sure if it's a bug or intended behavior with your non-existent project ID association, but one work-around is to have a beforeCreate hook in your models to verify that the project ID exists:
// In your Site model
beforeCreate: function(values, next) {
...
var projectID = values['project'];
Project.findOne(projectID, function (err, project) {
if (err || !project) return next("some error message");
return next();
});
}
You can also do a check in the beforeCreate hook for your second question:
// In your Site model
beforeCreate: function(values, next) {
...
var projectID = values['project'];
Project.findOne(projectID).populate('user').exec(function (err, project) {
if (err || !project) return next("some error message");
if (project.user.id != values['userID']) return next("some other error message");
return next();
});
}
Note that you'll have to pass 'userID' as a param into the params for creating a Site instance.

Avoiding global leaking when using JayData 1.3.4 local item store

Out of the box Entities defined by using $data.Entity.extend will be globally accessible. e.g. in the example taken from JayData's home page Todo will leak.
// Case 1: local item store example from http://jaydata.org/
$data.Entity.extend("Todo", {
Id: { type: "int", key: true, computed: true },
Task: { type: String, required: true, maxLength: 200 },
DueDate: { type: Date },
Completed: { type: Boolean }
});
console.log('Leaks Todo?', typeof window.Todo !== 'undefined');
//Result: true
In a JayData forum post I found a reference to $data.createContainer(), which can be used as container during Entity definition. In this case Todo2 won't leak.
// Case2: creating Todo2 in a container
$data.Entity.extend("Todo2", container, {
Id: { type: "int", key: true, computed: true },
Task: { type: String, required: true, maxLength: 200 },
DueDate: { type: Date },
Completed: { type: Boolean }
});
console.log('Leaks Todo2?', typeof window.Todo2 !== 'undefined');
//Result: false
Unfortunately after accessing stores there'll be other variables that leak globally even if the Entity itself is associated with a container.
console.log('Before store access: Leaks Todo2_items?',
typeof window.Todo2_items !== 'undefined');
//Result: false
$data('Todo2').save({ Task: 'Initialized Todo2'})
console.log('After store access: Leaks Todo2_items?',
typeof window.Todo2_items !== 'undefined');
//Result: true
Complete fiddle can be found at http://jsfiddle.net/RainerAtSpirit/nXaYn/.
In an ideal world every variable that is created for entities that run in a container would be associated with the same container. Is there an option to accomplish that or is the behavior described in Case2 the best that can be currently accomplished?