Badrequest in JSON SCHEMA with 'oneOf' - json

I am using the package express-jsonschema to validate the body of the request.
My schema is:
{
type: 'object',
oneOf: [
{ $ref: "#/definitions/one" },
{ $ref: "#/definitions/two" },
{ $ref: "#/definitions/three" }
],
definitions: {
one: {
type: 'string'
},
two: {
type: 'string'
}
three: {
type: 'string'
}
}
The body of my request is:
{
"one": "asdf"
}
The result of my request is a BadRequest, I have read this example Example JSON SCHEMA, but my code doesnt work.
Thank for your help.

Your request is an object with a property, not a string. So each of the definitions (or at least the definition of "one"'s) type should be an object with a property, not a string.
This is a valid schema for your request and might be what you are trying to do (don't have enough info to be sure):
{
type: 'object',
oneOf: [
{ $ref: "#/definitions/one" },
{ $ref: "#/definitions/two" },
{ $ref: "#/definitions/three" }
],
definitions: {
one: {
type: 'object',
properties: {
one: {
required: true,
type: "string"
}
}
},
two: {
type: 'object',
properties: {
two: {
required: true,
type: "string"
}
}
},
three: {
type: 'object',
properties: {
three: {
required: true,
type: "string"
}
}
}
}
}

Related

Json Schema allowing additional properties even though additionalProperties is set to false

Given the following schema:
export const MESSAGE_SCHEMA = {
additionalProperties: false,
type: 'object',
properties: {
comment: { type: 'string' },
startAt: { type: 'string' },
states: {
type: 'object',
minProperties: 1,
patternProperties: {
"^[A-Za-z]+[A-Za-z0-9 ]{0,127}$": {
type: 'object',
properties: {
type: { type: 'string', enum: TASK_TYPES_ALL_ENUM },
next: { type: 'string' },
end: { type: 'boolean' },
choices: {
type: 'array',
items: {
type: 'object'
},
minItems: 2
},
default: { type: 'string' },
error: { type: 'string' },
cause: { type: 'string' },
resource: { type: 'string' },
},
required: ['type'],
allOf: [
// Make choices required if task type = 'choice'
{
if: {
properties: { type: { const: TASK_TYPE_CHOICE } }
},
then: {
required: ['type', 'choices']
}
}
]
}
},
additionalProperties: false
}
},
required: ['startAt','states']
};
If I send a payload into my API with a key within the "states" object that doesn't match the pattern, it always allows the request... The behavior as I understood should prevent this with the properties / patternProperties + additionalProperties = false, but this is not the case...
For example - this should error since the pattern does not match and no additional properties are allowed, but I get a response from the API as though it was validated successfully:
{
"eventNamespace": "state",
"eventType": "transition",
"payload": {
"foo": "bar"
},
"message": {
"startAt": "foo",
"states": {
"!##^#^": {
"type": "choice",
"choices": []
}
}
}
}
If I then put a value that matches the pattern, I get a validation failure (as expected):
{
"eventNamespace": "state",
"eventType": "transition",
"payload": {
"foo": "bar"
},
"message": {
"startAt": "foo",
"states": {
"valid property example": {
"type": "choice",
"choices": []
}
}
}
}
Response:
{
"statusCode": 400,
"error": "Bad Request",
"message": "body/message/states/valid property example/choices must NOT have fewer than 2 items"
}
Your schema is fine. When I run it through my validator I get:
{
"errors" : [
{
"error" : "additional property not permitted",
"instanceLocation" : "/states/!##^#^",
"keywordLocation" : "/properties/states/additionalProperties"
},
{
"error" : "not all additional properties are valid",
"instanceLocation" : "/states",
"keywordLocation" : "/properties/states/additionalProperties"
},
{
"error" : "not all properties are valid",
"instanceLocation" : "",
"keywordLocation" : "/properties"
}
],
"valid" : false
}
I suggest you open a bug report for the implementation you are using.
Most likely, this is because Fastify validator removes additional properties by default.
You can disable this behaviour by setting removeAdditional: false:
const server = Fastify({
ajv: {
customOptions: {
removeAdditional: false
}
}
})
For more information https://www.fastify.io/docs/latest/Reference/Validation-and-Serialization/

Json Schema validate reference property on same object

Using JSON Schema 7 to perform validations
Is the below validation possible using json schema.
{
properties : [{name: "a"}, {name: "b"}, {name: "c"}],
rules : [{ prop : ["a","b"] }, { prop : ["a"] }, {prop: ["c"]}]
}
The "prop" property in object is dependent values in properties.
ie only of "properties.name" exists then that value can be added to the "prop" array
Note:
The "properties" array can have any object of type {name : }
"name" can have any possible string, which i don't know beforehand
I have been going through documentation, but can find a answer.
Is this validation not supported in Json Schema yet?
You can't do it with a static JSON schema.
To archive it you would need a dynamic schema validation, but this could be dangerous to code injection from malicious users:
const Ajv = require('ajv')
const ajv = new Ajv({ allErrors: true, jsonPointers: true })
const data = {
properties: [{ name: 'a' }, { name: 'b' }, { name: 'c' }],
rules: [{ prop: ['a', 'b'] }, { prop: ['a', 'zz'] }, { prop: ['c'] }]
}
const validProp = data.properties.map(_ => _.name)
const schema = {
type: 'object',
required: ['properties', 'rules'],
properties: {
properties: {
type: 'array',
items: {
type: 'object',
required: ['name'],
properties: {
name: { type: 'string' }
}
}
},
rules: {
type: 'array',
items: {
type: 'object',
required: ['prop'],
properties: {
prop: {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
enum: validProp // here happen the validation
}
}
}
}
}
}
}
const isValid = ajv.validate(schema, data)
if (!isValid) {
console.log(ajv.errors)
}

SharePoint data does not display in Kendo Scheduler

Data is retrieved using the REST call in my transport, but the event does not display in Kendo Scheduler. I am interested in only displaying data at this point, the update is not working
$("#scheduler").kendoScheduler({
date: new Date("2018/11/11"),
startTime: new Date("2018/11/11 07:00 AM"),
height: 600,
views: [
"day",
"workWeek",
"week",
{ type: "month", selected: true },
"agenda",
{ type: "timeline", eventHeight: 50}
],
add: function (e) {
},
change: function (e) {
},
error: function (e) {
//TODO: handle the errors
alert(e.errorThrown);
},
dataSource: {
transport: {
read: {
url: "https://SharePoint URL/apps/crp/_api/web/lists/getbytitle('list name')/items?$expand=Author&$select=Author/Id,Author/Title,Title,Start1,OData__x0045_nd1,RecurrenceRule,RecurrenceParentID,CategoryDescription,IsAllDay&$filter=Start1 ge datetime'2018-11-01T00:00:00Z'",
beforeSend: function (xhr) {
xhr.setRequestHeader("Accept", "application/json; odata=verbose");
}
},
add: function (e) {
},
error: function (e) {
//TODO: handle the errors
alert(e.errorThrown);
},
update:
{
url: function (data) {
alert('updating');
return "https://SharePoint URL/_api/web/lists/getbytitle(listname)/items" + "(" + data.ID + ")";
},
type: "POST",
dataType: "json",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"If-Match": "*",
"X-HTTP-Method": "MERGE",
},
},
parameterMap: function (data, type) {
if (data.models) {
alert(kendo.stringify(data.models));
return {
models: kendo.stringify(data.models)
}
}
}
},
schema: {
model: {
id: "ID",
fields: {
ID: { from: "ID", type: "number" },
title: { from: "Title", defaultValue: "No title", validation: { required: true } },
start: { type: "date", from: "Start1" },
end: { type: "date", from: "OData__x0045_nd1" },
recurrenceRule: { from: "RecurrenceRule" },
recurrenceId: { from: "RecurrenceParentID", type: "number" },
description: { from: "CategoryDescription" },
isAllDay: { type: "boolean", from: "IsAllDay" } //,
}
}
}
}
});
Reviewing the JSON call, data is retrieved properly but does not display in the Scheduler. I tried capturing events in the datasource, that does not look like it is processed, so the datasource does not seem to populate (change or add events).
Data returned looks like this after JSON call:
"{\"d\":{\"results\":[{\"__metadata\":{\"id\":\"Web/Lists(guid'2abecf66-35ed-4c67-b1f1-8b7255ebf0e2')/Items(1)\",\"uri\":\"https://SharePoint url/_api/Web/Lists(guid'2abecf66-35ed-4c67-b1f1-8b7255ebf0e2')/Items(1)\",\"etag\":\"\\"4\\"\",\"type\":\"SP.Data.6001C5110ListItem\"},\"Author\":{\"__metadata\":{\"id\":\"43c25e84-bf91-4d7d-951f-c480d9b2173f\",\"type\":\"SP.Data.UserInfoItem\"},\"Id\":5,\"Title\":\"SP USer\"},\"Title\":\"6001-C5-110\",\"Start1\":\"2018-11-14T15:00:00Z\",\"OData__x0045_nd1\":\"2018-11-14T17:00:00Z\",\"RecurrenceRule\":null,\"RecurrenceParentID\":null,\"CategoryDescription\":\"My Description\",\"IsAllDay\":null}]}}"
Added data: to the schema section of the datasource, thank you Sandun for pointing me in right direction, I defined the model for the scheduler without specifying the data.
schema: {
data: function (data) {
return data.d && data.d.results ? data.d.results : [data.d];
},
model: {
id: "ID",
fields: {
ID: { from: "ID", type: "number" },
title: { from: "Title", defaultValue: "No title", validation: { required: true } },
start: { type: "date", from: "Start1" },
end: { type: "date", from: "OData__x0045_nd1" },
recurrenceRule: { from: "RecurrenceRule" },
recurrenceId: { from: "RecurrenceParentID", type: "number" },
description: { from: "CategoryDescription" },
isAllDay: { type: "boolean", from: "IsAllDay" } //,
// startTimeZone: "Etc/UTC",
// endTimeZone: "Etc/UTC"
// description: { from: "Description" }
}
}
}

Extjs simple model and store

I have a simple model, let's say:
Ext.define('UserModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'firstname', type: 'string'},
{name: 'lastname', type: 'string'}
]
});
And a json file that looks like this:
{
"DatabaseInJSON": {
"Users": [
{
"KeyFirstName": "John",
"KeyLastName": "Doe"
},{
"KeyFirstName": "James",
"KeyLastName": "Howlett"
}
],
"OtherStuffWeDontCareAbout": [
...
]
}
}
My question is:
If I create a store like this, how can i map the attribute "firstname" from my model to "KeyFirstName" from my json ?
Ext.define('my.custom.Store', {
extend: 'Ext.data.Store',
model: 'UserModel',
proxy: {
type: 'ajax',
url: 'path/to/my/file.json',
reader: {
type: 'json',
rootProperty: 'DatabaseInJSON'
}
}
});
You need to either employ mapping or a convert function
Have a look at the demo here which demonstrates both in action.
For the sake of the demo I turned your store into a memory proxy store and you are I presume also accessing your rootProperty wrong as it should be rootProperty: 'DatabaseInJSON.Users'
Code:
Ext.application({
name: 'Fiddle',
launch: function() {
myData = {
"DatabaseInJSON": {
"Users": [{
"KeyFirstName": "John",
"KeyLastName": "Doe"
}, {
"KeyFirstName": "James",
"KeyLastName": "Howlett"
}],
"OtherStuffWeDontCareAbout": {}
}
};
Ext.define('UserModel', {
extend: 'Ext.data.Model',
fields: [{
name: 'firstname',
mapping: 'KeyFirstName',
type: 'string'
}, {
name: 'lastname',
convert: function(v, record) {
return record.data.KeyLastName;
},
type: 'string'
}]
});
Ext.define('my.custom.Store', {
extend: 'Ext.data.Store',
model: 'UserModel',
proxy: {
type: 'memory',
reader: {
type: 'json',
rootProperty: 'DatabaseInJSON.Users'
}
}
});
myStore = Ext.create('my.custom.Store', {
data: myData
});
console.log(myStore.getRange());
}
});
Generally your Json properties should match the same names of your fields so that the reader can read them properly, to map 'KeyFirstName' to 'firstname' I think your best option would be to create a mapping in the field definition on the model.
This would apply this globally for all requests and I believe it would reverse the mapping when it come to saving the data.
To use the mapping in your case, you would need something like:
Ext.define('UserModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'firstname', type: 'string', mapping: function(data) { return data.KeyFirstName; } },
{name: 'lastname', type: 'string', mapping: function(data) { return data.KeyLastName; } }
]
});
Other than changing the format of the JSON data, the only other way I can think of would be to override the read or getResponseData method of the JsonReader

Sencha Touch 2.2 load store from JSON, data goes to raw column

I'm trying to load a store from JSON received from webservices. But all the data from the JSON goes under the 'raw' column of the items in the store...
I can't figure out why, my code seems correct.
Any help is welcome.
My Model :
Ext.define('App.model.Node', {
extend: 'Ext.data.Model',
config: {
fields: [
{ name: 'id', type: 'int' },
{ name: 'version', type: 'int' },
{ name: 'user_id', type: 'int' },
{ name: 'tstamp', type: 'date' },
{ name: 'changeset_id', type: 'int' },
{ name: 'tags', type: 'string' },
{ name: 'geom', type: 'string'}
],
idProperty: 'id'
}
});
My Store :
Ext.define('App.store.NodeStore', {
extend: 'Ext.data.Store',
xtype: 'nodestore',
requires: [
'Ext.data.proxy.Rest'
],
config: {
model: 'App.model.Node',
storeId: 'nodeStore',
autoLoad: true,
proxy: {
type:'rest',
url:'http://localhost/server/nodes',
reader: {
type:'json',
rootProperty: 'nodes'
},
noCache: false,
limitParam: false,
headers: {
'Accept' : 'application/json'
}
}
}
});
My JSON :
{
"nodes": [
{
"id": "454467",
"version": 6,
"user_id": 52015,
"tstamp": "2008-12-27 21:38:45",
"changeset_id": "634766",
"tags": "",
"geom": "0101000020E6100000409CD1A0B29321405455682096804740"
},
{
"id": "454468",
"version": 8,
"user_id": 52015,
"tstamp": "2009-12-23 20:47:15",
"changeset_id": "3437205",
"tags": "",
"geom": "0101000020E6100000357C0BEBC69321409EC02ACD9C804740"
},
{
"id": "454469",
"version": 7,
"user_id": 52015,
"tstamp": "2009-12-23 20:47:15",
"changeset_id": "3437205",
"tags": "",
"geom": "0101000020E6100000347914F8D4932140B8BBBD5AA4804740"
}
]
}
And when I do a
var nodeStore = Ext.getStore('nodeStore');
nodeStore.load();
console.log(nodeStore.getData());
we can see the following object, with my data in the raw column under items...
I figured it out, my code is correct and the only thing missing is a callback in the load() function :
nodeStore.load({
callback: function(records, operation, success) {
console.log(records);
console.log(nodeStore.getCount());
nodeStore.each(function(element) {
console.log(element.data.id);
});
},
scope: this,
});
The problem was I was trying to access the store before it loaded the data. Now I'm waiting that all the data is loaded to access it, and it works.