ui-data contracts: client side extended validation of json data - javascript

I am in a few situations where json I am getting from services and database calls, created by another group, are giving me invalid data combinations, and causing many unintended errors downstream.
Given in the small example below, if the "rowContent" field is equal to "1", it's corresponding "row" needs to be a populated javascript object. "rowContent1" and "row1", and "rowContent2" and "row2" are correct. "rowContent3" and "row3" is not.
I concede the structure of this json is not fantastic. Ok it's a little wacky. It's fairly close to what I am dealing with in production. I have little control over it.
Are there data driven ways to describe json data relationships like this, that validate, before I start trying to use non-existent data in "row3"?
Or, what would you recommend I do in this situation?
thanks much,
-Larry
{ "table" : [
{
"aRowContent" : {
"rowContent1" : "1",
"rowContent2" : "0",
"rowContent3" : "1",
},
"row1" : {
"myRowValue" : "red"
},
"row2" : null,
"row3" : null
}
]
}

Not with JSON Schema, certainly. JSON Schema validates the structure of JSON data without cross-referencing to other bits of data.
I think the issue might be the redundancy in your data structure- why do you need /table/0/aRowContent/rowContent1 when you can deduce the same information from a null-check of /table/0/row1?

Related

Is it possible to move a particular JSON key to the top of the JSON using Javascript/VuejJS/Nodejs package?

I have a web application built using the Vuejs within that I am obtaining a JSON from the Java backend service which has a format something like this:
{
"a" : "Value-A",
"c" : "Value-C",
"b" : "Value-B",
"schema" : "2.0"
}
Before displaying the result to the user I would like to move the schema to the top so that it would be easy for the user to read and I want it to look something like this:
{
"schema" : "2.0",
"a" : "Value-A",
"c" : "Value-C",
"b" : "Value-B"
}
As we can see only schema position has changed, the rest of the JSON as it is.
Please Note:
I am aware the JSON order does not matter but I am doing this for better readability purposes. If there is a way then it would be really useful for the reader to understand the JSON better.
I want to know if there is a direct way to do it rather than looping over the JSON, as my created JSON in the real application can be pretty large.
All I want to do is move the schema to the top of the JSON. The rest of the JSON can be as it is I do not want to make any modifications to it.
Is there a way to do this using vanilla Javascript or using some Nodejs library as I am using the Vuejs?
I would really appreciate it if there was a way to do it or is there any workaround for this.
A very simplistic approach could be to stringify a new object.
const myObject = {
"a" : "Value-A",
"c" : "Value-C",
"b" : "Value-B",
"schema" : "2.0"
};
console.log(
JSON.stringify({
schema: myObject.schema,
...myObject
}, null, 2)
);
My suggestion is almost the same as the accepted answer but without using JSON.stringify():
const myObject = {
"a" : "Value-A",
"c" : "Value-C",
"b" : "Value-B",
"schema" : "2.0"
};
const myReorderedObject = {
schema: '',
...myObject
};
console.log(myReorderedObject);

Logic App bypass Null in filter query

I am building a Logic App that uses the Azure Resource connector to obtain a list of my resources. I would then like to filter the results to Microsoft.Compute resources that have a tag name and value of stop and normal.
Here is a snippet of resource that I receive back without any filters
{
"id": "/subscriptions/<subscription>/resourceGroups/Env1/providers/Microsoft.Compute/virtualMachines/MyVM1",
"name": "MyVM1",
"type": "Microsoft.Compute/virtualMachines",
"location": "westeurope",
"tags": {
"stop": "normal"
}
},
{
"id": "/subscriptions/<subscription>/resourceGroups/LogicApp/providers/Microsoft.Logic/workflows/DWSize-Check",
"name": "DWSize-Check",
"type": "Microsoft.Logic/workflows",
"location": "westeurope",
"tags": {}
}
As you can see, the bottom resource does not contain any tags, as with many others that will appear in the list
I use the standard Compose Filter Array connector to try and filter out from the value I receive back.
Here is the code that I wish to use for the filter command:
#and(contains(item()?['id'], '/Microsoft.Compute/virtualMachines/'), contains(item()?['tags'], variables('TagName')), contains(item()?['tags'], variables('TagValue')))
variables('TagName') and variables('TagValue') will be stop and normal, as show in the example tags listed in my results snippet.
However, because there is no tag values listed in other resources, such as Microsoft.Logic/workflows, I receive the following null error:
InvalidTemplate. The execution of template action 'Filter_array'
failed: The evaluation of 'query' action 'where' expression
'#contains(item()?['tags'], variables('TagValue'))' failed: 'The
template language function 'contains' expects its first argument
'collection' to be a dictionary (object), an array or a string. The
provided value is of type 'Null'.'.
Would anyone know how to get around this?
I have tried similar queries to this #contains(item().tags?.stop, variables('TagValue')) just to see if it picks up anything, but I'm still blocked by a null response :(
I tried the above with the help of the Workflow Definition Language, but still no dice. Any help would be greatly appreciated.
EDIT
In addition to the answer posted by Thomas, I have performed the following (image below) to filter out null from the results and get to the TagName, but I am still unable to get to the TagValue, even if I use contains:
#and(contains(item()?['tags'], variables('TagName')), contains(item()?['tags'], variables('TagValue')))
or event just trying to look for TagValue
#contains(item()?['tags'], variables('TagValue'))
You can check for null and return an empty value (an empty array in your case).
You can replace item()?['tags'] with this expression or create a variable :
if(equals(item()?['tags'], null), [], item()?['tags'])

Backbone Model get attribute function returns last updated value

If there's an optional field in the server returned JSON top structure. Backbone Model seem to cache previously set value. Lets say i get a JSON like this
{
label: "test_label",
attr1: "test1",
attr2: "test2"
}
when I say #model.get("label") i get "test_label". So later on, if i get a JSON like this
{
attr1: "test1",
attr2: "test2"
}
i get "test_label" when i query for #model.get("label"). Is this a known issue in backbone.js? I do something like this to fetch
#modelXhr = #model.fetch
success: (-> this.trigger('reset')).bind #model
I'm a beginner in javascript/coffeescript, What can i do so when i query for a field which doesn't exist in the latest returned model I won't get an older value? Appreciate your help
You should either clear the model beforehand using #model.clear() or (in my opinion the better way) ensure that the data format for a specific model type does not change. Return { label: null, ... }
You are then later on able to check for the existence of the label using #model.get("label")?
If you are not able to ensure data integrity throughout your requests, clear the model.

Backbone-relational: Association key won't work unless it's the same as the foreign key

I'm trying to get the backbone-relational plugin working with an association between tasks and messages. (A task has many messages).
The information is pulled from a standard rails/activerecord site, which has a task_id field as the foreign key.
The problem is, backbone-relational won't populate the 'messages' field with any messages on teh Task model unless I set the key as "task_id" in the reverse relation...but that means that, when accessing the task from the Message model, the task_id field is populated with the actual task object, not the 'task_id' integer, which is overwritten.
I'm guessing there's a simple way to specify task_id as the foreign key with which to determine the parent task, yet have the object that key represents placed in a different field (eg 'task' on the messages object)...but I can't figure out how. Any ideas appreciated. Code below
class Backbonescaffolddemo.Models.Task extends Backbone.RelationalModel
paramRoot: 'task'
relations: [{
type: Backbone.HasMany,
key: "messages",
relatedModel: "Backbonescaffolddemo.Models.Message",
collectionType: "Backbonescaffolddemo.Collections.MessagesCollection",
includeInJSON: true
reverseRelation: {
key: "task_id"
includeInJSON: true
}
}]
You may be able to use keySource or keyDestination to address your particular problem.
Example
In the following example, suppose we are getting data from an old-school relational database, where there is a one-to-many relationship between Monster and Loot_Item. This relationship is expressed by a Monster_Id foreign key in the Loot_Item table. Let us also suppose that our REST service doesn't do any fancy-pants data nesting for us, since that seems to match the situation in your question fairly closely.
keySource
Now, let's set set "keySource" to my foreign key ("Monster_Id") and "key" to the name of the attribute where I want the actual data to go (say, "Monster"). If you break in the debugger, you will see in the attributes object that there is, in fact, a field called "Monster", and that it does point to the monster model data. Hey, cool!
includeInJSON
However, if you toJSON that puppy, guess what? It has put all the monster data in Monster_Id, just like you didn't want! GAH! We can fix that by setting "includeInJSON" to "Monster_Id". Now, when it is converted to JSON, it puts the proper ID back into the Monster_Id field, when it is serializing your data to JSON, to send up to the server.
Problem solved? Er, well, actually, not necessarily...
CAVEAT: This all sounds super-useful, but there's one fairly glaring problem that I have found with this scenario. If you are using a templating engine (such as the one in Underscore.js) that requires you to convert your model to JSON, before passing it into the template, whoops -- you don't have access to your relational data. Alas, the JSON that we want for our messages is not necessarily the same JSON that we want to feed into our templates.
If you want the "task_id" in the message JSON to be the id, not the full JSON for the task, then set the "includeInJSON" to be the Task's ID property ("task_id")
class Backbonescaffolddemo.Models.Task extends Backbone.RelationalModel
paramRoot: 'task'
relations: [{
type: Backbone.HasMany,
key: "messages",
relatedModel: "Backbonescaffolddemo.Models.Message",
collectionType: "Backbonescaffolddemo.Collections.MessagesCollection",
includeInJSON: true
reverseRelation: {
key: "task_id"
includeInJSON: "task_id"
}
}]
The "true" value for includeInJSON says to use the full JSON for the related model.
Edit: After re-reading your question, I'm not sure my answer relates to your issue.
My original answer is for posting a message back to the server where you want the JSON to be something like:
{
"message_title": "My Title",
"message_body": "Blah blah blah...",
"task_id": 12345
}
I'm not sure what exactly you're looking to happen, but the way that Backbone Relational is supposed to work is that the Task's collection of messages will be a collection of the full models, so you can iterate over them and pass them to views for rendering, etc.
If you want to output one of the Message's id's in a template or something, then you'd take the Message model's "id":
myTask.get('messages').first().id -> returns the first message's id

Backbone relational lazy loading

I'm using Backbone with my RESTful JSON API in order to create an app that works with posts and their comments. I've been trying to get Backbone Relational to work, but run into problems with my Lazy loading.
I load up a list of posts, without the related comments. On click of an post in the list, I open up a view that fetches the full Post, comments included, and renders this.
I've got 2 Backbone.RelationModels, Posts and Comments. The post relation to the comment is setup as folows:`
relations: [{
type: Backbone.HasMany,
key: 'comments',
relatedModel: 'Comment',
includeInJSON: true, // don't include it in the exporting json
collectionType: 'Comments'
}]
Now the problem I'm facing is that the relationships are initialized as soon as I retrieve my list, that do not contain the comments yet. I load up the full data later, by fetching the model by it's URI. However, the relationships don't seem to reinitialise, calling Posts.get(1).get('comments') returns a Comments collection that is empty!
Does anyone know how I could best tackle this problem? The data is there, it just seems the collection of the comments doesn't gets updated.
Edit: I can make the Post model bind it's change:comments to itself, which updates the collection. However, I can't seem to find a solid way to get the original comments' JSON, since this.get('comments') returns the Comments collection.
Note: In my collection, I do parse the JSON to make it work with my API, with the following code:
parse: function(response) {
var response_array = [];
_.each(response, function(item) {
response_array.push(item);
});
return response_array;
}
This is because the JSON returned by my API returns an object with indexed keys (associative array) instead of a native JSON array.
{
"id" : "1",
"title" : "post title",
"comments" : {
"2" : {
"id" : "2",
"description": "this should solve it"
},
"6" : {
"id" : "6",
"description": "this should solve it"
}
}
}
Thanks a bunch! Please ask any questions, I'm sure I've been vague somewhere!
The Backbone Relational model doesn't parse collections other then arrays, the JSON from my question didn't work. I changed the backend to return the comments in a proper array
{
"id" : "1",
"title" : "post title",
"comments" : [
{
"id" : "2",
"description": "this should solve it"
},
{
"id" : "6",
"description": "this should solve it"
}]
}
}
The RelationalModel doesn't respect the parse function that Backbone provides to parse your JSON before it moves on. With the backend returning "proper" JSON, the lazy loading works without any extra code.
You can also use the initialize method on your comment model, to simulate the parse method and define attributes with custom values like this (CoffeeScript):
initialize: (obj) ->
#attributes = obj.customKey

Categories

Resources