I'm working on an API and should return based on permissions only a subset of the actual object's properties. I'm writing my tests in mocha and chai and would like to test for something like this (given res is the response object from the server and res.body contains the received JSON data):
res.body.should.not.contain.properties.except(['prop1', 'prop2.subprop'])
in which case res.body.prop1 can be any kind of object, and res.body.prop2 is only allowed to contain the property subprop - which again could be any kind of object.
Now, I could write custom functions to test this, but I thought someone else had already a similar problem and there is an extension for chai for it maybe or some other library I could use instead.
Out of the box, I do not think Chai offers a way to build a query like this. However, a JSON Schema is a perfect fit for testing if an object matches a certain format. Luckily, a Chai JSON Schema Plugin exists. Using that, the result looks like this:
chai.use(require('chai-json-schema'));
var bodySchema = {
title: 'response body',
type: 'object',
required: ['prop1', 'prop2'],
additionalProperties: false,
properties: {
prop1: {},
prop2: {
type: 'object',
required: ['subprop'],
additionalProperties: false,
properties: {
subprop: {}
}
}
}
};
res.body.should.be.jsonSchema(bodySchema);
A short explanation:
The required property takes an array of required properties. If prop1 or prop2 are actually optional, remove them from this array (or leave it out alltogether).
The additionalProperties: false ensures no properties other than the ones defined in the properties hash are allowed.
prop2 contains a subschema, which can contain the same fields as the root schema and specifies the format of the sub-property.
Granted, these schema's can grow a bit large, but so would your validation function. Of course you can make the schema a true JSON file to separate it from the rest of your code.
Related
Should we send a null or empty string value on request ?
I mean we have an optional value and it had a value currently. If use want to delete value of that optional field, should API understand null or empty is delete value ?
Ex:
{
name: { type: String, required: true },
phone: { type: String, required: false }
}
In database:
{
name: "Alex",
phone: "012-333.222"
}
And now, use want to delete their phone number
Should we define looks like:
PUT /users/user-id-1
{
phone: null
}
Seems it's a bad convention
Should we send a null or empty string value on request ?
REST doesn't care; which is to say that it tells us to use self descriptive messages to transfer documents over a network, but it doesn't tell us what the representations of the documents should be.
Where you want to be looking instead is at message schema definitions, and in particular designing your schema in such a way that it can be extended in backwards compatible ways. The XML community spent a lot of time exploring those ideas; Orchard 2004 might be a good starting point.
In HTTP, the basic mechanism for describing a change to a resource is to use a PUT command with a copy of the new representation. So a request would probably look like:
PUT /users/user-id-1
Content-Type: application/json
{
name: "Alex",
phone: null
}
If your schema is defined in such a way that the phone field is optional, and that optional and null are equivalent (as opposed to some other implied value), then you might equivalently use:
PUT /users/user-id-1
Content-Type: application/json
{
name: "Alex"
}
In cases where the representation is very big, and the changes you are making are small, you might want to support PATCH.
PATCH /users/user-id-1
Content-Type: application/merge-patch+json
{
phone: null
}
Note that the HTTP PATCH specification includes the Allow-Patch which allows clients to discover which patch representations a server supports for a resource.
Though, in front end, usually it depends on whether use the delete button or, they just leave the field empty
phone: '' - means user left field empty
phone: null - means user click on delete field button. You decide whether to delete the field, or just set the document field to null.
I will usually delete the field, since it is now useless.
If you want to update only one property in a document you can use PATCH method instead of PUT method and your code should look like this:
PATCH /users/user-id-1
{
phone: ""
}
I'm having trouble understanding how to use a getter with a mongoose schema I'm using for an API. I'm using the ipaddr.js module for the ipaddr methods.
I have this field defined in my schema:
ipv4_address: {
type: Buffer,
required: false,
get: function () {
//For debugging, actually want it returned
console.log(this.ipv4_address.toString());
},
set: function (v) {
return ipaddr.parse(v).toByteArray();
},
select: false,
index: true
}
I've set getters to true so that I can see what's going on in the console, ultimately I just want the ipv4_address returned in the JSON result.
ClientSchema.set('toObject', {
getters: true
});
ClientSchema.set('toJSON', {
getters: true
});
I understand that this line:
console.log(this.ipv4_address.toString());
causes the setter to be called recursively resulting in a RangeError: Maximum call stack size exceeded. The reason for this I understand completely. If I were reading from _ipv4_address this wouldn't happen, but the schema defines the variable name.
This is apart of a REST API and I'd like the POSTs to write the ipv4_address as that field name. I'd like the field name to be the same when GETing the result.
I'm just very confused on how I should get around this.
The getter function is passed the current field value, so you don't need to pull it out of this, and you can avoid the recursion:
get: function (me) {
//For debugging, actually want it returned
console.log(me.toString());
},
I am currently trying to resolve a simple recipe list that has a reference to ingredients.
The data layout looks like this:
type Ingredient {
name: String!
amount: Int!
unit: Unit!
recipe: Recipe
}
type Recipe {
id: Int!
name: String!
ingredients: [Ingredient]!
steps: [String]!
pictureUrl: String!
}
As I understand it, my resolvers should look like this:
The first one resolves the recipes and second one resolves the ingredient field in the recipe. It can (from my understanding) use the argument provided by recipe. In my recipe object, the ingredient is referenced by id (int), so this should be the argument (at least that's what I think).
var root = {
recipe: (argument) => {
return recipeList;
},
Recipe: {
ingredients: (obj, args, context) => {
//resolve ingredients
}
},
These resolvers are passed to the app like this:
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
rootValue: root,
}));
However, my resolver does not seem to be called. I would like the ingredients to be resolved on the fly when queried in my query.
The endpoint works, but as soon as I query for ingredients, an error with this message "message": "Cannot return null for non-nullable field Ingredient.name.", is returned.
When trying to log the incoming arguments in my resolver, I can see that it is never executed. Unfortunately, I can't find examples on how to do this with express-graphql when using it like I am.
How do I write seperate resolvers for nested types in express-graphQL?
Only resolvers for queries and mutations can be defined through root, and even then this is bad practice. I'm guessing you're building your schema using buildSchema, which is generally a bad idea since the generated schema will only use default resolvers.
The only way to define resolvers for a field like ingredients when using plain GraphQL.js is to not use buildSchema. Instead of generating your schema from a string, you would define it programatically (i.e. defining a GraphQLSchema and all the types it uses).
Doing the above is a huge pain, especially if you already have your schema defined in a string or document. So the alternative option is to use graphql-tools' makeExecutableSchema, which lets you inject those resolvers into your type definitions like you're trying to do. makeExecutableSchema returns a GraphQLSchema object, so you can use it with your existing code (you don't have to change your middleware to apollo-server if you don't want to).
I am trying to build a MEAN project, so I need to validate some of my model's dynamic key...
I want to create a Schema like this
var exampleSchema = new Schema({
x: {
type: String,
default: '',
required: true,
trim: true
},
y: {}
});
as you see I have mixed type object, but actually it is a Language Map and it should be something like this,
{
"en-US": "answered"
}
can I validate my key with mongoose? (I think it has no function like that)
if no, how and where can I validate it (in model or controller)?
You may want to look into this: http://mongoosejs.com/docs/middleware.html
Specifically pre-save events. Mongoose gives you control over this and you can perform validation, mapping as needed before the actual model gets saved.
Also works nice for pre-init event if you need defaults such as "current date" for an audit trail such as "createdOn: date".
Sails have support very convenient model througth Waterline, and I have used the 'array' attribute type in the way storing many string, but now I want to store more complex object, Although I can store the raw data in mongo by 'array' type, I don't know if it is safe and I want define the object type in the array, like mongoose's style. for example, I need a model "Products" and I want define it as a 'array' but the object stored in the array only model "Book", what I can do like this, but I don't think it works.
module.exports = {
products : {
type : 'array',
Book : {
name : 'string',
price : 'integer'
}
}
}
So, any suggestion about use of the 'array' and 'json' is very appreciated, thanks so much!
I don't think that the array type is going to work the way you want it to. What you're looking for is associations which will be available in SailsJS 0.10 (which you can get via git right now). The array attribute type for MySQL and PostgreSQL it will only stringify the array and store it as text in the database then it will parse the field when returning the value.
source