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

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/

Related

ExtJS model associations with jsonapi specification

We are creating a new version our API (v2) adopting the JSON:API specification (https://jsonapi.org/). I'm not being able to port the ExtJS model associations (belongs_to) to the new pattern.
The ExtJS documentation only shows how to use a nested relation in the same root node (https://docs.sencha.com/extjs/4.2.2/#!/api/Ext.data.association.Association).
v1 data (sample):
{
"data": [
{
"id": 1,
"description": "Software Development",
"area_id": 1,
"area": {
"id": 1,
"code": "01",
"description": "Headquarters"
}
},
],
"meta": {
"success": true,
"count": 1
}
}
v2 data (sample):
{
"data": [
{
"id": "1",
"type": "maint_service_nature",
"attributes": {
"id": 1,
"description": "Software Development",
"area_id": 1
},
"relationships": {
"area": {
"data": {
"id": "1",
"type": "area"
}
}
}
}
],
"included": [
{
"id": "1",
"type": "area",
"attributes": {
"id": 1,
"code": "01",
"description": "Headquarters"
}
}
],
"meta": {
"success": true,
"count": 1
}
}
My model:
Ext.define('Suite.model.MaintServiceNature', {
extend: 'Ext.data.Model',
fields: [
{ desc: "Id", name: 'id', type: 'int', useNull: true },
{ desc: "Area", name: 'area_id', type: 'int', useNull: true },
{ desc: "Description", name: 'description', type: 'string', useNull: true, tableIdentification: true }
],
associations: [
{
type: 'belongsTo',
model: 'Suite.model.Area',
foreignKey: 'area_id',
associationKey: 'area',
instanceName: 'Area',
getterName: 'getArea',
setterName: 'setArea',
reader: {
type: 'json',
root: false
}
}
],
proxy: {
type: 'rest',
url: App.getConf('restBaseUrlV2') + '/maint_service_natures',
reader: {
type: 'json',
root: 'data',
record: 'attributes',
totalProperty: 'meta.count',
successProperty: 'meta.success',
messageProperty: 'meta.errors'
}
}
});
Any ideias on how to setup the association to work with the v2 data?
I'm honestly taking a stab at this one... I haven't used Ext JS 4 in years, and I wouldn't structure my JSON like JSON:API does, but I think the only way you can accomplish this is by rolling your own reader class. Given that you have generic properties for your data structure, this reader should work for all scenarios... although, I'm not too familiar with JSON:API, so I could be totally wrong. Either way, this is what I've come up with.
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.myReader',
root: 'data',
totalProperty: 'meta.count',
successProperty: 'meta.success',
messageProperty: 'meta.errors',
/**
* #override
*/
extractData: function (root) {
var me = this,
ModelClass = me.model,
length = root.length,
records = new Array(length),
dataConverter,
convertedValues, node, record, i;
for (i = 0; i < length; i++) {
node = root[i];
var attrs = node.attributes;
if (node.isModel) {
// If we're given a model instance in the data, just push it on
// without doing any conversion
records[i] = node;
} else {
// Create a record with an empty data object.
// Populate that data object by extracting and converting field values from raw data.
// Must pass the ID to use because we pass no data for the constructor to pluck an ID from
records[i] = record = new ModelClass(undefined, me.getId(attrs), attrs, convertedValues = {});
// If the server did not include an id in the response data, the Model constructor will mark the record as phantom.
// We need to set phantom to false here because records created from a server response using a reader by definition are not phantom records.
record.phantom = false;
// Use generated function to extract all fields at once
me.convertRecordData(convertedValues, attrs, record, me.applyDefaults);
if (me.implicitIncludes && record.associations.length) {
me.readAssociated(record, node);
}
}
}
return records;
}
});
Ext.define('Suite.model.Area', {
extend: 'Ext.data.Model',
fields: [{
name: 'type',
type: 'string'
}]
});
Ext.define('Suite.model.MaintServiceNature', {
extend: 'Ext.data.Model',
fields: [{
desc: "Id",
name: 'id',
type: 'int',
useNull: true
}, {
desc: "Area",
name: 'area_id',
type: 'int',
useNull: true
}, {
desc: "Description",
name: 'description',
type: 'string',
useNull: true,
tableIdentification: true
}],
associations: [{
type: 'belongsTo',
model: 'Suite.model.Area',
associatedName: 'Area',
foreignKey: 'area_id',
associationKey: 'relationships.area.data',
instanceName: 'Area',
getterName: 'getArea',
setterName: 'setArea'
}],
proxy: {
type: 'rest',
url: 'data1.json',
reader: {
type: 'myReader'
}
}
});
Suite.model.MaintServiceNature.load(null, {
callback: function (record) {
console.log(record.getData(true));
}
});
}
});

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" }
}
}
}

Badrequest in JSON SCHEMA with 'oneOf'

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"
}
}
}
}
}

EXTJS 5: Load child Nodes from the server on expand(TreePanel, load on demand)

I have a tree, which only loads 3 level at a time.
So when a user click on expand on a node, extjs will call a service and populate with the leaf nodes of that node. How can I acchieve this? For now, i'm only able to load all of the nodes at once, which is a huge performance hit if there is lot of nodes.
Model:
Ext.define("TreePanelResult", {
extend: 'Ext.data.TreeModel',
fields: [
{ name: "orgId", type: "int"},
{ name: "orgName", type: "string"},
{ name: "OrgShortName", type : "string"},
{ name: "iconCls", type: "string"},
{ name: "leaf", type: "bool" }
],
idProperty:'orgId'
});
ViewModel:
Ext.define('SampleViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.sampleTreeModel',
stores: {
treeStore: {
type: 'tree',
model:TreePanelResult,
root: {
text: 'Org',
id:'src',
expanded: true,
orgId: -1
},
paramNode:'orgId',
proxy: {
type: 'ajax',
url: 'services/admin/getHirerachy',
reader : {
type : 'json',
rootProperty : 'systemChildNodes'
}
},
listeners: {
beforeload: function(store, operation, eOpts) {
operation.params.node = operation.node.get("orgId");
store.getProxy().setExtraParams("orgId", operation.node.get("orgId"));
}
}
}
}
});
JSON Stucture:
{
"status": true,
children:null,
"systemChildNodes": [
{
"orgId": "234334",
"orgParentId": "0",
"OrgShortName": "csdsdsd",
"iconCls": "",
"leaf": false,
"systemChildNodes": [
{
"orgId": "2345446",
"orgParentId": "234334",
"OrgShortName": "sdewew",
"iconCls": "",
"leaf": false,
"systemChildNodes": []
}
{
"orgId": "2345446",
"orgParentId": "234334",
"OrgShortName": "sdewew",
"iconCls": "",
"leaf": false,
"systemChildNodes": []
}
]
}]
}
Any suggestions would be appreciated.
Thanks in Advance,
Sankar.

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.