Reputation: 1589
How does one validate a model with a property, of type object in Sails JS?
I know that attributes with a simple value (e.g. string), is acceptable, however how does this work for nested JSONs?
Like the following:
{
name: 'John',
location: {
x: 23,
y: 15,
z: 50
}
}
So would it be of the form:
{
name: {
type: 'string',
required: true,
},
location: {
x: {
type: 'number',
required: true
},
y: {
type: 'number',
required: true
},
z: {
type: 'number',
required: true
}
}
}
Upvotes: 5
Views: 2405
Reputation: 1812
I have made a structure comparison function to handle such cases, i'd like to share with you, hope it inspires & help:
assuming you are using sails v1
Create a file in ./api/services
called ModelService.js
with the below code:
module.exports = {
invalidStructure(schema, input) {
try {
if (schema.type === 'array') {
// Invalid: if input is not array
// OR some of input[items] doesn't match schema.item
return !_.isArray(input) ||
_.some(input, item => this.invalidStructure(schema.item, item));
}
else if (schema.type === 'object') {
// Invalid if input is not an object
// OR if input.keys doesn't match schema.struct.keys
// OR if typeof input[key] doesn't match schema.struct[key]
return !_.isObjectLike(input) ||
!_.isEqual(_.keys(schema.struct), _.keys(input)) ||
_.some(_.keys(input), key => this.invalidStructure(schema.struct[key], input[key]));
}
else { // verifying field value vs schema.type
// TODO: Add other field validations here (i.e. isEmail, required,...etc.)
return typeof input !== schema.type;
}
}
catch (err) {
sails.log.error('Exception in [invalidStructure] : ', err);
return true;
}
}
}
and use it like this in your model:
const address = {
type: 'object',
struct: {
name: { type: 'string' },
location: {
type: 'object',
struct: {
x: { type: 'string' },
y: { type: 'string' },
z: { type: 'string' },
}
}
}
}
module.exports = {
attributes: {
address: {
type: 'json',
custom: value => !ModelService.invalidStructure(address, value)
}
}
}
Now i know that this doesn't handle or the cases (i.e. required: false
)
But it should get you going with matching json
structure and value types
Note: This is the source file. Feel free to use, or enhance with a PR
Upvotes: 0
Reputation: 657
The most elegant solution I have found so far is to using the "machine" library (which powers the actions in sailsjs, built by sailsjs people) to define type validation "machines".
First you need a type define helper:
$ cat api/types/define.js
const buildWithCustomUsage = require("machine").buildWithCustomUsage;
function validateWith(machine, inputs) {
machine(inputs).now();
return true;
}
module.exports = function (def) {
const machine = buildWithCustomUsage({
def,
extraArginsTactic: "doNotCheck"
});
return {
machine,
validate: validateWith.bind(null, machine)
};
};
Then you can define a type like this:
$ cat api/types/QuoteRequest.js
module.exports = require("./define")({
description: "Quote request type definition",
inputs: {
input_currency_type: {
description: "Input currency type",
type: "string",
required: true
},
amount_requested: {
description: "Requested amount in input currency",
type: "string",
required: true
}
},
sync: true,
fn: function (inputs, exits) {
// your other validation logics
return exits.success();
}
});
You need to make sure you set sync: true
.
To use it in your controllers, do like this:
inputs: {
request: {
type: "json",
required: true,
custom: require("../../types/QuoteRequest").validate
}
},
Hope it helps!
Upvotes: 1
Reputation: 24958
Waterline (the Sails ORM) doesn't directly support nested schemas. You can use a custom validation rule to validate the attribute instead:
module.exports = {
types: {
location: function(val) {
// Make sure that x, y and z are present and are numbers.
// This won't allow numeric strings, but you can adjust to fit your needs.
return (_.isNumber(val.x) && _.isNumber(val.y) && _.isNumber(val.z));
}
},
attributes: {
location: {
type: 'json',
required: true, // If you want the whole attribute to be required
location: true // Validate that the attribute has the schema you want
}
...more attributes...
}
};
Upvotes: 5