JSON schema for data description vs data validation vs input validation - json

In what I can find about using JSON schema, there seems to be a confusing conflation of (or at least a lack of distinction among) the tasks of describing valid data, validating stored data, and validating input data.
A typical example looks like:
var schema = {
type: 'object',
properties: {
id: { type: 'integer', required: true },
name: { type: 'string', required: true },
description: { type: 'string', required: false }
}
};
This works well for describing what valid data in a data store should look like, and therefore for validating it (the latter isn't terribly useful—if it's in a store it should be valid already):
var storedData = {
id: 123,
name: 'orange',
description: 'delicious'
};
It doesn't work that well for validating input. id is most likely left for the application to generate and not for the user to provide as part of the input. The following input fails validation because it lacks the id which the schema declares to be required:
var inputData = {
name: 'orange',
description: 'delicious'
};
Fine, one might say, the schema isn't meant to validate direct input, validation should only occur after the application added an id and the data is what is meant to be stored.
If the schema isn't meant to validate direct input, however, what is 1) the point of JavaScript validators running in the browser, presumably being fed direct input and 2) the point of the obviously input-oriented readonly schema feature in the spec?
Ground gets shakier when thinking of properties that can be set once but not updated (like a username), as well as different access levels (e.g. the admin and the owner of the orange should be able to change the description, while for other users it should stay readonly).
What is the best (or at least working) practice to deal with this? A different schema for each use case, like below?
var baseSchema = {
type: 'object',
properties: {
id: { type: 'integer', required: true },
name: { type: 'string', required: true },
description: { type: 'string', required: false }
}
};
var ownerUpdateSchema = {
type: 'object',
properties: {
id: { type: 'integer', required: false, readonly: true },
name: { type: 'string', required: true },
description: { type: 'string', required: false }
}
};
var userUpdateSchema = {
type: 'object',
properties: {
id: { type: 'integer', required: false, readonly: true },
name: { type: 'string', required: false, readonly: true },
description: { type: 'string', required: false, readonly: true }
}
};
Or something else?

Side-note: "required" is now an array in the parent element in v4, and "readOnly" is capitalised differently - I'll be using that form for my examples
I agree that validating the stored data is pretty rare. And if you're just describing the data, then you don't need to specify that "id" is required.
Another thing to say is that these schemas should all have URIs at which they can be referenced (e.g. /schemas/baseSchema). At that point, you can extend the schemas to make "id" required in some of them:
var ownerInputSchema = {
type: 'object',
properties: {
id: {type: 'integer', readOnly: true},
name: {type: 'string'},
description: {type: 'string'}
},
required: ['name']
};
var userInputSchema = {
allOf: [{"$ref": "/schemas/inputSchema"}],
properties: {
name: {readOnly: true}
}
};
var storedSchema = {
allOf: [{"$ref": "/schemas/inputSchema"}],
required: ["id"]
}
Although, as I said above, I'm not sure storedSchema should be necessary. What you end up with is one "owner" schema that describes the data format (as served, and as editable by the data owner), and you have a secondary schema that extends that to declare readOnly on an additional property.

Well, I think the purpose of Json-Schema is more clearly defined in v4. The goal is assist you in a data structure validation (whether it is going to be stored, it has been sent to you across the wire, or you are creating in an interactive fashion).
readOnly is not a Json-Schema validation property because it has not validation constraints. In Json-Schema v4 readOnly is part of the hyper-schema definition. It can be used to express that you can not change this property in a POST request.
Json-schema does not define how you should implement the interaction with the user, if you allow for transitory "bad" data, or if any error has to be corrected before you can add more data to the system. This is up to you.

Related

Sails.js Can't create data in another model from other model's lifecycle

Hey I'm kinda new to Sails. I've enabled REST api in sails and no I just simply create data using the body of the post request body without the intervention of a controller.
I have defined two models - users, and call_log. Every time an event occurs in my app, I want that to trigger a change in the value of a user in the users table but I also want to create a log in the call log table.
I'm using an afterCreate() lifecycle method and try to use the create() in order to write my data to the second table. I don't seem to get any error, but nothing is written to the dB.
my User.js (model)
module.exports = {
attributes: {
username: {type: 'string', required: true},
country: {type: 'string'},
},
afterCreate: function (valuesToSet, proceed) {
Talk_log.create({
uid: 'myuid',
partner_uid: 'mypartnerid',
action: 'yeahhh'
}).fetch();
valuesToSet.password = 1244;
return proceed();
},
};
my Talk_log.js
module.exports = {
attributes: {
uid: { type: 'string', required: true},
partner_uid: {type: 'string'},
action: {type: 'string'}
},
};
Documentation say "The afterCreate lifecycle callback will only be run on queries that have the fetch meta flag set to true"
So use:
User.create().fetch();
Fetch tell Waterline (and the underlying database adapter) to send back records that were updated/destroyed/created when performing an .update(), .create(), .createEach() or .destroy() query.
U say afterCreate() but in your code is beforeCreate(). Fix that.
I forgot the .then() that handles the bluebird promises and only then I could write to dB.
Talk_log.update({ uid: valuesToSet.uid})
.set({ joined: true})
.fetch()
.then(() => {return proceed();});

ExtJS4, tree.Panel null using a json proxy

I am trying to use Ext.tree.Panel with an Ext.data.TreeStore and an Ext.data.Model. I think I am doing things right but when checking my tree I can see a beatiful 'null' and that is not normal. And I think it could be because of my json url.
Let me explain how I am proceeding.
Here is how I define my Model:
Ext.define('SelectedModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'text', type: 'string'},
{name: 'id', type: 'string'},
{name: 'leaf', type: 'bool'},
{name: 'children', type: 'auto'},
{name: 'singleClickExpand', type: 'bool'},
{name: 'status', type: 'int'},
{name: 'checked', type: 'boolean'},
{name: 'fullIceCode', type: 'string'},
{name: 'withMenu', type: 'bool'},
]
});
I read that fields 'leaf' and 'children' are present by default in all trees (with 20 others fields). So I am not sure I have to mention them. All these fields are precisely those presents in my json response (however they are not always all present in every item, like you will see in my example below). As I don't really understand how ExtJS works (this is my first week of practicing) I am wondering if I really need to write in my Model all the fields present in my json response, or just those which are present in every item of my tree.
Let see how I define my Store in my Tree:
store: Ext.create("Ext.data.TreeStore",{
model: 'SelectedModel',
proxy: {
type: 'ajax',
url: this.myaction,
reader: {
type: 'json',
}
}
})
At the end of the Tree constructor, if I write a console.log(this.myaction) I obtain exactly what I want which is 'stSearchAction.do?action=product_tree'.
This url is the one that set the json response to the servlet. It leads to this method:
private ActionForward getSpecialToolProductTree(
HttpServletRequest request, HttpServletResponse response) throws SystemException, ApplicativeException {
if (strJsonProduct == null)
{
strJsonProduct = getAndCreateSessionIfNeeded(request).getStrJsonProductSelector();
}
System.out.println(strJsonProduct);
setResponse(response, strJsonProduct) ;
return null ;
}
The System.out.println(strJsonProduct) you can see shows me precisely the json string I want:
[{text : 'CASE CONSTRUCTION', id : '5 -122001754' , leaf : false , children : [{text : 'Engines', ... ... ... ... ... ... ..., singleClickExpand : true , status : 1 , checked : false , fullIceCode : 'Y.A.01.001', withMenu : true }
] , singleClickExpand : true , status : 1 }] , singleClickExpand : true , status : 1 }] , singleClickExpand : true , status : 1 }]
I don't see for now where is the problem, but I admit that I can be pretty blind sometimes.
Now the Tree:
Ext.define('Application.TreePanel', {
extend: 'Ext.tree.Panel',
requires: ['Ext.data.TreeStore'],
myaction: 'Unknown Url',
myrender: 'Unknown Render',
xtype: 'check-tree',
rootVisible: false,
useArrows: true,
frame: true,
title: 'Unknown title',
width: 250,
height: 300,
constructor: function(myaction, mywidth, myheight, myrender, mytitle) {
if (myaction) {
this.myaction = myaction;
}
if (myrender) {
this.myrender = myrender;
}
if (mytitle) {
this.title = document.getElementById(mytitle).innerHTML;
}
if (mywidth) {
this.width = mywidth;
}
if (myheight) {
this.height = myheight;
}
console.log(this.height);
},
config:{
renderTo: this.myrender,
store: Ext.create("Ext.data.TreeStore",{
model: 'SelectedModel',
proxy: {
type: 'ajax',
url: this.myaction,
reader: {
type: 'json',
}
}
})
}
});
When I check the different attributes set in my constructor with some console.log, I got exactly what I want...
All the extJs code I showed you is in a same file, tree421.js (because using ExtJS 4.2.1).
What I think is there is a problem with my store, I am not doing the right thing, maybe this is not the way of using the url config for a proxy... but I can't find the good way.
I obtain some strange results.
When executing these lines at the end of my tree421.js
var tree = Ext.create("Application.TreePanel", 'stSearchAction.do?action=product_tree', 300, 270, 'tree-div', 'lbl_st_tree_selection');
console.log(tree);
console.log(tree.getSelectionModel());
console.log(tree.getRenderTo());
console.log(tree.getRootNode());
I can read
j {myaction: "stSearchAction.do?action=product_tree", myrender: "tree-div", title: "Product selection", width: 300, height: 270…} tree421.js:117
A {events: Object, views: Array[0], modes: Object, selectionMode: "SINGLE", selected: A…} tree421.js:118
undefined tree421.js:119
null tree421.js:120
The second log is just a test to see what would be written but I don't really care. We can see that the tree.getRenderTo() is undefined, but I put it in config block so getters & setters should be generated, and a console.log(this.myrender) in the constructor wrote something good, so I don't understand what is going on.
And last but not least my tree.getRootNode() is null...
It would be appreciated if someone with a better ExtJS understanding could advice me. I feel like there is something wrong with my way of using json url but I don't understand why. Or maybe it is something else...
Thank you for your time.
Poney
Ok so I worked on others trees from less important projects to get familiar with this concept and with javascript and EXTJS.
So now I come back on this one and I can say that it worked fine in fact but I was not correctly getting the informations after the tree construction.
The reason is that I am working on the migration of some projects from extjs-3.2.1 to extjs-4.2.1 and the whole tree system has changed. The operations node.attributes.something I used to make to get informations from a node don't work anymore and I have to pass through node.data.somethingor node.raw.something. That's all.
I just had to precise a reader and a root to my tree too.
Anyway here is the essential part of the tree:
Ext.define('SelectedModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'text', type: 'string'},
{name: 'id', type: 'string'},
{name: 'leaf', type: 'bool'}
]
});
var tree = Ext.create('Ext.tree.TreePanel',{
renderTo:myrender,
title: document.getElementById(mytitle).innerHTML,
height: myheight,
width: mywidth,
useArrows:true,
autoScroll:true,
animate:true,
ddConfig:{
enableDrag:false,
enableDrop:false
},
containerScroll: true,
rootVisible: false,
frame: true,
store: Ext.create('Ext.data.TreeStore',{
model: 'SelectedModel',
proxy:{
type: 'ajax',
url: myaction,
reader: {
type: 'json'
},
root: {
type: 'async'
}
},
autoLoad: false
});
So there was not any problem, I just had to practice a little javascript and extjs4 and get used to javascript debugging. :)
Now I can appreciate it a little more.
Thank you

Nested JSON objects in Sencha Touch store using CakePHP backend API

I am using Sencha Touch 1.1 to build a bar locator. I am using CakePHP as my backend API and I want to be able to use the data as Cake outputs it.
Viewing the JSON data below you will see that the Pub data is returned in the 'Pub' array and the Suburb in a similar way. So accessing the data would be done as follows:
Pub.id, Pub.name, Pub.address_1
Does anyone know how I can use this format in my Sencha model and store?
I have my model and store setup as follows:
Ext.regModel('Pub', {
fields: ['id', 'name', 'address_1', 'address_2', 'marker', 'lat', 'lng', 'suburb']
});
Ext.regStore('NearbyStore', {
model: 'Pub',
sorters: 'suburb',
getGroupString: function(record) {
return record.get('suburb');
},
proxy: {
type: 'scripttag',
url: 'http://exampleurl/bars/nearby.json?lat=-55.8874&lng=-11.177',
reader: {
type: 'json',
root: 'results'
}
},
autoLoad: true
});
Below is the JSON data that is returned from my store proxy.
stcCallback1001({"results":[{"Pub":{"id":"125","name":"Brownsville Bowling & Recreation Club","address_1":"31a malouffst","address_2":"-","marker":"default_marker.png","lat":"-33.887402","lng":"151.177002"},"Suburb":{"name":"Knoxville"},"0":{"distance":"0.0002511751890598611"}},{"Pub":{"id":"1721","name":"Hampshire Hotel","address_1":"91 parramatta rd","address_2":"-","marker":"default_marker.png","lat":"-33.886799","lng":"151.177002"},"Suburb":{"name":"Brownsville"},"0":{"distance":"0.06684402352323478"}}]});
you should see
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Field-cfg-mapping
similar question on stackoverflow :
Accessing nested objects in JSON feed - Sencha Touch
So you should map your model like this :
Ext.regModel('Pub', {
fields: [
{
name: 'id',
type: 'string',
mapping: 'Pub.id'
},
{
name: 'name'
type: 'string',
mapping: 'Pub.name'
},
{
name: 'address_1',
type: 'string',
mapping: 'Pub.address_1'
},
{
name: 'AND SO ON.......'
}
});
Well, I've just started on sencha touch. So I hope im helping.. :)
you will not only have to make these, but also map filtering, remote sorting, success properties, validation errors, and so on. If you are planning a bigger project I would recommend you to switch to Sencha Touch 2 and use Bancha.
Then you will not have to do any of those things. And as a nice bonus you can simply expose your CakePHP models and don't have to write them manually again for Sencha Touch.

In ExtJS, how to define jsonreader for different json input formats?

I have an external source that generates JSON in given format. I need to use ScriptTagProxy to access that data. Format of data is (two records):
{
"COLUMNS":["KEDBID","EMPID","KE","SOLUTION","ATTACH_KE","ATTACH_SOLUTION"],
"DATA":[
[1,36661,"While adding a new user, if his\/her profile is missing, it could be LDAP server issue.","Contact Mr. ABC from infra team to get user's profile corrected on server","screenshots.jpg","conacts.doc"],
[2,36661,"The error code # 123445 is trivial that may occur at particular time.","To resolve this issue, we will need to ask DBA team to refresh database. That will fix this type of errors","NA","NA"]
]
}
How to define jsonreader for this data? As i have seen that root should be like:
{ROOT: [ {field1:data1,field2:data1},{field1:data2,field2:data2}]}
while my JSON is like:
{fieldnames:['field1','field2'],
ROOT:[ [data1ForField1,data1ForField2],[data2ForField1,data2ForField2] ]
}
UPDATE
Adding one more case:
Similarly can we have a jsonreader for;
{"ROWCOUNT":8,
"COLUMNS":["CATID","CATEGORY"],
"DATA":{"CATID":[1,2,3,4,5,6,7,8],"CATEGORY":["Optimization","Automation","Process Improvement","Tool","Other","Another One","ThisHas'","More^!##(){}"]}}
where 1-->Optimization, 2-->Automation, 3-->Process Improvement etc.
Basically I need to return data from ColdFusion query object by serializing query object. Serialization in CF Query can return data in above two formats.
I'm still new to Ext World!! Hope to get some support.
Best Regards,
Tushar Saxena
Below is the code where I'm facing issue. The data is being coming up in store as it is available to ComBox.. but not able to read data using each function or by other means? So is it that Data in store can only be fed to components? In another example I created a simplestore (ArrayStore) paased data manually using loadDATA().. there each function was working!!!
In below case to if I add one record after load using ADD(), each function would get executed one time showing data that I just added... and that just added data is not available to components!!! How come!!!
May be I'm missing some very basic concept as I'm still very new to EXT.
Would be really greatful for response.
var proxy = new Ext.data.ScriptTagProxy({
url: 'http://127.0.0.1:8500/extex/kebyid.cfm',
method: 'POST'
});
var rec = Ext.data.Record.create([
{name: 'EMPID', mapping: 1},
{name: 'KE', mapping: 2},
{name: 'SOLUTION', mapping: 3},
{name: 'ATTACH_KE', mapping: 4},
{name: 'ATTACH_SOLUTION', mapping: 5}
]);
var myReader = new Ext.data.ArrayReader({
idIndex: 0,
root: 'DATA'
}, rec);
var store = new Ext.data.ArrayStore({
proxy: new Ext.data.HttpProxy({
url: '/extex/kebyid.cfm',
method: 'POST'
}),
autoSave: true,
autoLoad: true,
root: 'DATA',
fields: [
{name: 'KEID', mapping: 0},
{name: 'EMPID', mapping: 1},
{name: 'KE', mapping: 2},
{name: 'SOLUTION', mapping: 3},
{name: 'ATTACH_KE', mapping: 4},
{name: 'ATTACH_SOLUTION', mapping: 5}
]
});
store.each(function(){
alert('!!!!'); //**************NOT WORKING
});
var catCB = new Ext.form.ComboBox({
fieldLabel: 'Category',
layout:'absolute',
x: 100,
y: 5,
store: store, //************* DATA IS THERE AS STORE IS PROVIDING DATA TO COMBOBOX
emptyText:'Select Category...',
valueField : 'KEID',
displayField : 'KE',
hiddenName : 'category',
hiddenvalue : 'None',
mode: 'local',
editable : false,
lastQuery: '',
renderTo: Ext.get("d1"),
listeners: {
'beforequery': function(qe){
qe.forceAll = true;
}}
});
First of all, you cannot use your server's output for scriptTagProxy. scriptTagProxy utilises JSONP technology. So output should look like this:
callback({
"COLUMNS":["KEDBID","EMPID","KE","SOLUTION","ATTACH_KE","ATTACH_SOLUTION"],
"DATA":[
[1,36661,"While adding a new user, if his\/her profile is missing, it could be LDAP server issue.","Contact Mr. ABC from infra team to get user's profile corrected on server","screenshots.jpg","conacts.doc"],
[2,36661,"The error code # 123445 is trivial that may occur at particular time.","To resolve this issue, we will need to ask DBA team to refresh database. That will fix this type of errors","NA","NA"]
]
});
Then simply use ArrayStore as your store. It uses ArrayReader by default:
var store = new Ext.data.ArrayStore({
proxy: new Ext.data.ScriptTagProxy({
url: 'http://example.com/get_data.php'
}),
root: 'DATA',
fields: [
'id',
// ...,
// ...,
// ...
]
});
UPDATE
I forgot that you are using 'DATA' as root property. Added it to store config.

.NET DateTime serialised to JSON for use in ExtJs JsonStore(event store for ExtJs Calendar)?

I'm trying to set up a Json Store for an ExtJs Calendar.
The store uses a Http Proxy to retrieve it's data.
The stores fields include startDate and endDate which are objects of type date.
I'm serializing data in my C# code into Json which will be requested by the Http proxy.
I am wondering should I serialize the start and endates as a string or as C# DateTime type.
At the moment I am serialising them as DateTime types.
The Json response looks like this:
{"Data":
"items":[{
"cid":"0",
"end":"\/Date(1275260400000+0100)\/",
"notes":"4:00",
"start":"\/Date(1275260400000+0100)\/",
"title":"Basic""}]
The start and end properties look like some sort of date reference.
I have tried serialising the startDate and endDate's as strings rather than DateTime types.
This returns the following JsonResponse:
{"Data":
"items":[{
"cid":"0",
"end":"03/06/10",
"notes":"4:00",
"start":"04/06/10",
"title":"Basic""}]
However, in both cases when the store has finished loading the endDate and startDate fields are undefined.
What should I do here?
I was thinking maybe i have to format the dates into a certain format expected by extjs?
Below is some sample code:
this.eventStore = new Ext.data.JsonStore({
id: 'eventStore',
root: 'Data.items',
proxy: new Ext.data.HttpProxy({
url: AppRootPath + 'Calendar/GetCalendarData',
method: 'POST'
}),//proxy
fields: Ext.calendar.EventRecord.prototype.fields.getRange()
});
Check the documentation for Ext.data.Field - http://dev.sencha.com/deploy/dev/docs/?class=Ext.data.Field . It has a property named 'dateFormat' that allows you to specify the exact date format.
I had the same problem and I solved it like this:
fields: [
{ name: 'Id', type: 'int' },
{ name: 'ResourceId', mapping: 'fitter_id', type: 'int' },
{ name: 'StartDate', type: 'date', format: 'd/m/Y G:i' },
{ name: 'EndDate', type: 'date', format: 'd/m/Y G:i' },
{ name: 'status', type: 'int' },
{ name: 'job_id', type: 'int' }
]