Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sails JS: Validating model with embedded JSON property

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
        }
    }
}
like image 806
jhtong Avatar asked Sep 07 '14 13:09

jhtong


2 Answers

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...

  }

};
like image 128
sgress454 Avatar answered Oct 17 '22 03:10

sgress454


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!

like image 44
Miao ZhiCheng Avatar answered Oct 17 '22 03:10

Miao ZhiCheng