Why oneOf does not work on my schema in jsonschema? - jsonschema

My login has different payloads one is:
{
"username": "",
"pass": ""
}
And one of the other is:
{
"username": "",
"pass": "",
"facebook": true
}
And the last:
{
"username": "",
"pass": "",
"google": true
}
My schema is as follow:
login_schema = {
"title": "UserLogin",
"description": "User login with facebook, google or regular login.",
"type": "object",
"properties": {
"username": {
"type": "string"
},
"pass": {
"type": "string"
},
"facebook": {
"type": "string"
},
"google": {
"type": "string"
}
},
"oneOf": [
{
"required": [
"username",
"pass"
],
"additionalProperties": False,
},
{
"required": [
"username",
"pass"
"google"
]
},
{
"required": [
"username",
"pass",
"facebook"
]
}
],
"minProperties": 2,
"additionalProperties": False,
}
It should give an error for the below sample:
{
"username": "",
"pass": "",
"google": "",
"facebook": ""
}
But it validates the schema successfully! What I have done wrong in the above schema?
EDIT-1:
pip3 show jsonschema
Name: jsonschema
Version: 3.0.2
Summary: An implementation of JSON Schema validation for Python
Home-page: https://github.com/Julian/jsonschema
Author: Julian Berman
Author-email: Julian#GrayVines.com
License: UNKNOWN
Location: /usr/local/lib/python3.7/site-packages
Requires: setuptools, six, attrs, pyrsistent
EDIT-2:
What I get as an error is:
jsonschema.exceptions.ValidationError: {'username': '', 'pass': '', 'google': '12'} is valid under each of {'required': ['username', 'pass', 'google']}, {'required': ['username', 'pass']}
A live demo of the error: https://jsonschema.dev/s/mXg5X

Your solution is really close. You just need to change /oneOf/0 to
{
"properties": {
"username": true,
"pass": true
},
"required": ["username", "pass"],
"additionalProperties": false
}
The problem is that additionalProperties doesn't consider the required keyword when determining what properties are considered "additional". It considers only properties and patternProperties. When just using required, additionalProperties considers all properties to be "additional" and the only valid value is {}.
However, I suggest a different approach. The dependencies keyword is useful in these situations.
{
"type": "object",
"properties": {
"username": { "type": "string" },
"pass": { "type": "string" },
"facebook": { "type": "boolean" },
"google": { "type": "boolean" }
},
"required": ["username", "pass"],
"dependencies": {
"facebook": { "not": { "required": ["google"] } },
"google": { "not": { "required": ["facebook"] } }
},
"additionalProperties": false
}

Related

Generic json schema for two condition based responses

I have an API returning response based on input type.
For a single filter I get
{
"intervalData": [
{
"timestamp": "2021-05-25T06:00:00.000Z",
"result": {
"Attempts": 309194
}
}
],
"intervalTotals": {
"Attempts": 4719471
}
}
For multiple filter I get
{
"multiFilterData": [
{
"filter": {
"id": 111111
},
"intervalData": {
"timestamp": "2021-05-25T20:41:39.000Z",
"result": {
"Attempts": 7902
}
}
},
{...}
]
}
That means the response will either have (intervalData,intervalTotals) OR multiFilterData.
Question:
Is it possible to have a generic schema to handle this or must I have two separate schemas?
How to make sure that if one combination is present other should not be there?
I tried with below schema on https://www.jsonschemavalidator.net/. The validation fails if there is some other keys listed under the required, unfortunately the validation succeeds if one of the entry from oneOf is missing. EDIT1 below
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"oneOf": [
{
"required": [
"intervalData",
"intervalTotals",
]
},
{
"required": [
"multiFilterData",
]
}
],
"properties": {
"intervalData": {
"type": "array",
"additionalItems": false,
"items": {
"type": "object",
"required": [
"timestamp",
"result"
],
"properties": {
...
},
"additionalProperties": false
}
},
"intervalTotals": {
"type": "object",
"required": [
"Attempts"
],
"properties": {
"Attempts": {
"type": "integer"
}
},
"additionalProperties": false
},
"multiFilterData": {
"type": "array",
"additionalItems": false,
"items": {
"type": "object",
"required": [
"filter",
"intervalData"
],
"properties": {
"filter": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer"
}
},
"additionalProperties": false
},
"intervalData": {
"type": "object",
"required": [
"timestamp",
"result"
],
"properties": {
"timestamp": {
"type": "string"
},
"result": {
"type": "object",
"required": [
"Attempts"
],
"properties": {
...
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
EDIT 1: This schema also passes even though the first required is missing an element on the list.
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"oneOf": [
{
"required": [
"intervalData"
]
},
{
"required": [
"multiFilterData",
]
}
],
"properties": {
"intervalData": {...},
"intervalTotals": {...},
"multiFilterData": {...}
},
"additionalProperties": false
}

Pass an already existing Model to next in Loopback

There is Project model
{
"name": "Project",
"plural": "Projects",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"title": {
"type": "string",
"required": true
},
"description": {
"type": "string"
},
"code": {
"type": "string"
},
"startDate": {
"type": "date",
"required": true
},
"endDate": {
"type": "date"
},
"value": {
"type": "number"
},
"infoEN": {
"type": "string"
},
"infoRU": {
"type": "string"
},
"infoAM": {
"type": "string"
},
"externalLinks": {
"type": [
"string"
]
}
},
"validations": [],
"relations": {
"industry": {
"type": "belongsTo",
"model": "Industry",
"foreignKey": "",
"options": {
"nestRemoting": true
}
},
"service": {
"type": "belongsTo",
"model": "Service",
"foreignKey": "",
"options": {
"nestRemoting": true
}
},
"tags": {
"type": "hasAndBelongsToMany",
"model": "Tag",
"foreignKey": "",
"options": {
"nestRemoting": true
}
}
},
"acls": [],
"methods": {}
}
And it hasAndBelongsToMany tags
here is Tag model
{
"name": "Tag",
"plural": "Tags",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
Now when the relation is created loopback api gives this api endpoint.
POST /Projects/{id}/tags
This creates a new tag into the tags collection and adds it to the project.
But what about adding an already existing tag to the project?
So I figured maybe I add before save hook to the Tag
Here I'll check if the tag exists and then pass the existing one for the relation.
Something like this.
tag.js
'use strict';
module.exports = function(Tag) {
Tag.observe('before save', function(ctx, next) {
console.log(ctx.instance);
Tag.find({name: ctx.instance.name})
next();
});
// Tag.validatesUniquenessOf('name', {message: 'name is not unique'});
};
#HaykSafaryan it just demo to show you how to use tag inside project
var app = require('../../server/server');
module.exports = function(project) {
var tag=app.models.tags
//afterremote it just demo. you can use any method
project.afterRemote('create', function(ctx, next) {
tag.find({name: ctx.instance.name},function(err,result)){
if(err) throw err;
next()
}
});
};
this is just example code to show you how to use update,create ,find ,upsertwithwhere etc. tag for validation you have to setup condition over here it will not take validation which you defined in tags models

Failed to execute 'send' on 'XMLHttpRequest' for only one url in Loopback

Strongloop/Loopback node.js server used with 'ng-admin' editor and sqlite db. I need to get count of entities:
var Httpreq = new XMLHttpRequest();
Httpreq.open('GET', yourUrl, false);
Httpreq.send(null);
return Httpreq.responseText;
where yourUrl is like http://localhost:3000/api/v1/entity/count.
All urls works except one of entity named 'Advertisement', I have angular.js:12783 Error: Failed to execute 'send' on 'XMLHttpRequest': Failed to load http://localhost:3000/api/v1/advertisement/count. This url works in API explorer.
Advertisement.json:
{
"name": "Advertisement",
"base": "EntityBase",
"plural": "Advertisement",
"options": {
"validateUpsert": true,
"sqlite3": {
"table": "advertisement"
}
},
"properties": {
"name": {
"type": "string",
"required": true
},
"category_id": {
"type": "number"
},
"due_date": {
"type": "string"
},
"from_date": {
"type": "string"
},
"phone": {
"type": "string"
},
"site": {
"type": "string"
}
},
"validations": [],
"relations": {
"category": {
"type": "belongsTo",
"model": "Category",
"foreignKey": "category_id"
},
"photos": {
"type": "hasMany",
"model": "Photo",
"foreignKey": "advertisement_id"
}
},
"acls": [],
"methods": {},
"mixins": {
"Timestamp": {},
"SoftDelete": {},
"GenderAge": {},
"Descripted": {}
}
}
Renamed entity to Commercial to fix this.

Json schema dynamic key validation

Facing an issue with schema validation.
schema :
{
"type": "object",
"$schema": "http://json-schema.org/draft-03/schema",
"id": "#",
"required": true,
"patternProperties": {
"^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,6}$": {
"type": "object",
"required": true,
"properties": {
"_from": {
"id": "_from",
"type": "string",
"required": true
},
"message": {
"type": "object",
"id": "message",
"properties": {
"detail": {
"type": "string",
"id": "detail",
"required": true
},
"from": {
"type": "string",
"id": "from",
"required": true
}
}
}
}
}
}
}
json :
{
"tom#example.com": {
"_from": "giles#gmail.com",
"message": {
"from": "Giles#gmail.com",
"detail": "AnyonewanttomeetmeinParis"
}
},
"harry#example.com": {
"_from": "giles#gmail.com",
"message": {
"from": "Giles#gmail.com",
"detail": "AnyonewanttomeetmeinParis"
}
}
}
Here the key email address is dynamic, somehow it doesn't validate regex for email validation.
Can you please advise me to correct the schema.
I am validating using : http://json-schema-validator.herokuapp.com/index.jsp
I see in your pattern that you seem to have forgotten to escape some characters or didn't do it correctly:
"^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,6}$"
and it causes the error that you can see when you hover the mouse over the link at the top of the validator:
it should be:
"^[A-Z0-9\\._%\\+-]+#[A-Z0-9\\.-]+\\.[A-Z]{2,6}$"
or without escaping the inner/class characters but I'd use the first pattern because I think its intention is clearer:
"^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,6}$"
You need to have two \ because the first \ is an escape for the second \. With a single one it wouldn't work because there is no escape sequence like \. or \+ in javascript. You want to have a \in the pattern itself.
However json schema patternProperties are case sensitive by default so you need to extend your email pattern by adding a-z to it:
"^[A-Za-z0-9\\._%\\+-]+#[A-Za-z0-9\\.-]+\\.[A-Za-z]{2,6}$"
(I didn't find any other way to make it case insensitive)
You also need to exclude any other property names by adding "additionalProperties": false next to the patternProperties or otherwise it catches everything else that does not match the pattern.
The working schema should then look like this:
{
"type": "object",
"$schema": "http://json-schema.org/draft-03/schema",
"id": "#",
"required": true,
"patternProperties": {
"^[A-Za-z0-9\\._%\\+-]+#[A-Za-z0-9\\.-]+\\.[A-Za-z]{2,6}$": {
"type": "object",
"required": true,
"properties": {
"_from": {
"id": "_from",
"type": "string",
"required": true
},
"message": {
"type": "object",
"id": "message",
"properties": {
"detail": {
"type": "string",
"id": "detail",
"required": true
},
"from": {
"type": "string",
"id": "from",
"required": true
}
}
}
}
}
},
"additionalProperties": false
}
I've tested it on: http://jsonschemalint.com/
Changed the schema as per draft 04 :
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema",
"patternProperties": {
"^[A-Za-z0-9\\._%\\+-]+#[A-Za-z0-9\\.-]+\\.[A-Za-z]{2,6}$": {
"type": "object",
"properties": {
"__from": {
"type": "string"
},
"message": {
"type": "object",
"properties": {
"from": {
"type": "string"
},
"detail": {
"type": "string"
}
},
"required": [ "from","detail"]
}
},
"required": [ "__from","message"]
}
},
"additionalProperties": false
}

JSON Schema for collections

I am looking for how to write a JSON schema for collection of objects within an object.
{
"name": "Sadiq",
"age": 68,
"email": [
{
"emailid": "sadiq#gmail.com"
},
{
"emailid": "sadiq#yahoo.com"
}
],
"phone": [
{
"phonenumber": "301-215-8006"
},
{
"phonenumber": "301-215-8007"
}
]
}
Here is one possible way to write this schema:
{
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"required": true
},
"age": {
"type": "integer",
"required": true
},
"email": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"emailid": {
"type": "string",
"required": true
}
}
}
},
"phone": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"phonenumber": {
"type": "string",
"required": true
}
}
}
}
}
}
Possible improvements would be:
Add a regex pattern to strongly validate the emailid field
Extract email and phone into top level types and refer to them in the above schema.
You can have a try of csonschema, which let you write jsonschema with a more easier way.
name: 'string'
age: 'integer'
email: ['email']
phone: ['string']
In python, there is json library which can help you encode or reformat as u need