Operator "missing" not working properly in JsonLogic - json

I am using JsonLogic to validate my input payload with the rules defined using JsonLogic. I am able to test the rules using "Play with it" tool and my rules work perfectly fine against my input data.
But when I run the same rules through my .net Core application by passing payload from Postman the rules always return the else condition even when it should the error from if condition.
{
"if": [
{
"missing": [
"ProposedProjectDetails.IsFreezone",
"ProposedProjectDetails.InterestedToLeaseFrom",
"ProposedProjectDetails.IndustryType",
"ProposedProjectDetails.OtherType",
"ProposedProjectDetails.ProjectDescription",
"ProposedProjectDetails.OutputofFacility",
"ProposedProjectDetails.ProductionCapacity",
"ProposedProjectDetails.ProductionCapacityVolume",
"ProposedProjectDetails.MainRawMaterials",
"ProposedProjectDetails.RawMaterialQuantity",
"ProposedProjectDetails.RawMaterialEstimatedCost",
"ProposedProjectDetails.RawMaterialEstimatedTotInvestment",
"ProposedProjectDetails.AnnualSalesRevenue",
"ProposedProjectDetails.ConstructionStartDate",
"ProposedProjectDetails.Skilledjobs",
"ProposedProjectDetails.TotalAccomodationRequired",
"ProposedProjectDetails.TotalWorkerSalary",
"ProposedProjectDetails.EBITDA",
"ProposedProjectDetails.PortCargoImports",
]
},
"Missing mandatory inputs",
"all good"
]
}
Sample input payload is
{
"companyId": "string",
"serviceCode": "IPA",
"serviceType": "string",
"serviceName": "string",
"crmApplicationId": "string",
"crmReferenceNumber": "string",
"portalReferenceNumber": "string",
"data": {
"proposedProjectDetails": {
"outputofFacility": 2,
"productionCapacity": 0,
"productionCapacityVolume": 0,
"others": "string",
"shiftsPerDay": 0
}
}
}
My .Net code which is evaluating this is
public ValidationResponse Validate(JObject validation, JObject data)
{
var evaluator = new JsonLogicEvaluator(EvaluateOperators.Default);
var result = evaluator.Apply(validation, data);
return new ValidationResponse
{
IsValid = (string.Equals("All Fine", result.ToString(), StringComparison.OrdinalIgnoreCase)),
Message = result
};
}
When I run above code with JsonRules and above Payload, I always get all good in the response. But since I am missing required data in payload, it should get the error Missing mandatory inputs which I do get in JsonLogic "Play with it" tool.

Try keeping the names as json mapping fields camel case once like below
"if": [
{
"missing": [
"proposedProjectDetails.isFreezone",
"proposedProjectDetails.interestedToLeaseFrom",
]
},
"Missing mandatory inputs",
"all good"
]
}

Related

Sending a specific entity to FIWARE QuantumLeap

In my application I have a very specific entity format, where one of the attributes' values is a json array of values.
{
"id": "Proximity3",
"type": "SensorAgent",
"measurementType": {
"type": "string",
"value": "boolean",
"metadata": {}
},
"modifiedTime": {
"type": "string",
"value": "2020-06-08T12:30:11.091506Z",
"metadata": {}
},
"readings": {
"type": "array",
"value": [
{
"type": "SensorReading",
"value": {
"reading": {
"type": "boolean",
"value": false
}
}
}
],
"metadata": {}
},
"sanID": {
"type": "string",
"value": "SAN_3",
"metadata": {}
},
"sensorID": {
"type": "string",
"value": "Proximity3",
"metadata": {}
},
"sensorManufacturer": {
"type": "string",
"value": "Unknown",
"metadata": {}
},
"sensorType": {
"type": "string",
"value": "ON_OFF_SENSOR",
"metadata": {}
}
}
]
As you can see, readings attribute has an array of values stored in it (in this example only one.
Whenever I try to subscribe QuantumLeap to Orion Context Broker, subscription is successful, but there is no data received by QuantumLeap (even querying http://localhost:8668/v2/attrs gives me 'No records were found for such query.'). Tried using attrFormat on subscription, but unsuccessful.
At the same time, this https://quantumleap.readthedocs.io/en/latest/user/ suggests that the QuantumLeap expects a specific entity.
My question is, is there anything possible to do in order to get this entity to QuantumLeap, or I would need to change the entity?
It is clear from the QuantumLeap Data Insertion documentation that QuantumLeap expects the notification it receives to contain attributes passed as Native JSON types (like Number)
"temperature": {
"value": 24.2,
"type": "Number",
"metadata": {}
}
If your context broker holds attributes in a different way, direct notification will always fail, but there is nothing stopping you from proxying the notification and reformatting it in the manner that QuantumLeap prefers.
Create a simple subscription listener to read the current subscription data,
reformat the bits you need and forward to Quantum Leap and return the received HTTP success/failure back to the context broker.
const request = require('request');
function receiver(req, res) {
let data = [];
// Manipulate the data
req.body.data.readings.value.forEach((reading) => {
data.push[reading.value]
});
// Copy the rest of data from the original subscription
// Then Forward to QuantumLeap - replace with real values
const options = {
url: '/subscription/to/quantum/leap',
method: 'POST',
json: data,
headers:
};
// Respond back to context broker with response from QL
request(options, (error, response, body) => {
return error ? res.send(error) : res.status(204).send();
});
}

In Logic Apps JSON Array while parsing throwing error for single object but for multiple objects it is working fine

While parsing JSON in Azure Logic App in my array I can get single or multiple values/objects (Box as shown in below example)
Both type of inputs are correct but when only single object is coming then it is throwing an error "Invalid type. Expected Array but got Object "
Input 1 (Throwing error) : -
{
"MyBoxCollection":
{
"Box":{
"BoxName": "Box 1"
}
}
}
Input 2 (Working Fine) : -
{
"MyBoxCollection":
[
{
"Box":{
"BoxName": "Box 1"
},
"Box":{
"BoxName": "Box 2"
}
}]
}
JSON Schema :
"MyBoxCollection": {
"type": "object",
"properties": {
"box": {
"type": "array",
items": {
"type": "object",
"properties": {
"BoxName": {
"type": "string"
},
......
.....
..
}
Error Details :-
[
{
"message": "Invalid type. Expected Array but got Object .",
"lineNumber": 0,
"linePosition": 0,
"path": "Order.MyBoxCollection.Box",
"schemaId": "#/properties/Root/properties/MyBoxCollection/properties/Box",
"errorType": "type",
"childErrors": []
}
]
I used to use the trick of injecting a couple of dummy rows in the resultset as suggested by the other posts, but I recently found a better way. Kudos to Thomas Prokov for providing the inspiration in his NETWORG blog post.
The JSON parse schema accepts multiple choices as type, so simply replace
"type": "array"
with
"type": ["array","object"]
and your parse step will happily parse either an array or a single value (or no value at all).
You may then need to identify which scenario you're in: 0, 1 or multiple records in the resultset? I'm pasting below how you can create a variable (ResultsetSize) which takes one of 3 values (rs_0, rs_1 or rs_n) for your switch:
"Initialize_ResultsetSize": {
"inputs": {
"variables": [
{
"name": "ResultsetSize",
"type": "string",
"value": "rs_n"
}
]
},
"runAfter": {
"<replace_with_name_of_previous_action>": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Check_if_resultset_is_0_or_1_records": {
"actions": {
"Set_ResultsetSize_to_0": {
"inputs": {
"name": "ResultsetSize",
"value": "rs_0"
},
"runAfter": {},
"type": "SetVariable"
}
},
"else": {
"actions": {
"Set_ResultsetSize_to_1": {
"inputs": {
"name": "ResultsetSize",
"value": "rs_1"
},
"runAfter": {},
"type": "SetVariable"
}
}
},
"expression": {
"and": [
{
"equals": [
"#string(body('<replace_with_name_of_Parse_JSON_action>')?['<replace_with_name_of_root_element>']?['<replace_with_name_of_list_container_element>']?['<replace_with_name_of_item_element>']?['<replace_with_non_null_element_or_attribute>'])",
""
]
}
]
},
"runAfter": {
"Initialize_ResultsetSize": [
"Succeeded"
]
},
"type": "If"
},
"Process_resultset_depending_on_ResultsetSize": {
"cases": {
"Case_no_record": {
"actions": {
},
"case": "rs_0"
},
"Case_one_record_only": {
"actions": {
},
"case": "rs_1"
}
},
"default": {
"actions": {
}
},
"expression": "#variables('ResultsetSize')",
"runAfter": {
"Check_if_resultset_is_0_or_1_records": [
"Succeeded",
"Failed",
"Skipped",
"TimedOut"
]
},
"type": "Switch"
}
For this problem, I met another stack overflow post which is similar to this problem. While there is one "Box", it will be shown as {key/value pair} but not [array] when we convert it to json format. I think it is caused by design, so maybe we can just add a record "Box" at the source of your xml data such as:
<Box>specific_test</Box>
And do some operation to escape the "specific_test" in the next steps.
Another workaround for your reference:
If your json data has only one array, we can use it to do a judgment. We can judge the json data if it contains "[" character. If it contains "[", the return value is the index of the "[" character. If not contains, the return value is -1.
The expression shows as below:
indexOf('{"MyBoxCollection":{"Box":[aaa,bbb]}}', '[')
The screenshot above is the situation when it doesn't contain "[", it return -1.
Then we can add a "If" condition. If >0, do "Parse JSON" with one of the schema. If =-1, do "Parse JSON" with the other schema.
Hope it would be helpful to your problem~
We faced a similar issue. The only solution we find is by manipulating the XML before conversion. We updated XML nodes which needs to be an array even when we have single element using this. We used a Azure function to update the required XML attributes and then returned the XML for conversion in Logic Apps. Hope this helps someone.

Nested JSON Schema Validation in Postman

I want to validate a Nested JSON Schema in Postman.
Here is the code.
const testSchema = {
"name": [
{
"first_name": "Alpha",
"last_name": "Bravo"
},
{
"first_name": "Charlie",
"last_name": "Delta"
},
],
"age": "23",
"color": "black"
};
const showData = {
"required": ["name", "age"],
"properties": {
"name": [
{
"required": ["first_name"]
}
],
},
};
pm.test("Nested Schema Test", function () {
pm.expect(tv4.validate(testSchema, showData)).to.be.true;
});
Currently, this code returns test as true.
I am unable to test the "name" array objects' keys.
Even upon passing this:
"required": ["fst_nae"] //wrong key name
it returns true.
I would just check in easy way via:
pm.test("your name", function () {
pm.expect(testSchema.name[0].first_name && testSchema.name[1].first_name
).to.eql('Alpha' && 'Charlie')
});
and you successfully validated these fields
or use this expect to organize your code of your choice
tiny validator i.e. tv4.validate is having issues in their library. Another option is to use AJV (you can search it on github).

Using JPath, how do I return a value based on another key value pair of the same node?

I have the following JSON file. It is truncated for brevity of this question. An actual JSON file would contain 10 - 20 messages and 3 - 15 results.
{
"messages": [
{
"type": "msgInfo",
"description": "Some stuff happened"
},
{
"type": "msgInfo",
"description": "More stuff happened"
},
{
"type": "msgInfo",
"description": "yup, more stuff happened"
}
],
"results": [
{
"parameterId": "val_1",
"dataType": "Double",
"value": 123.45
},
{
"parameterId": "val_2",
"dataType": "Double",
"value": 246.80
},
{
"parameterId": "val_3",
"dataType": "Double",
"value": 135.79
},
{
"parameterId": "val_4",
"dataType": "Long",
"value": 20161021
}
]
}
I'm trying to retrieve the value of the value key based on the value of the parameterId key. For example, I need to return "123.45" using a JPath to "val_1".
So far I have this code (copied from this post) but I can't get the path correct.
JObject obj = JObject.Parse(json);
JToken token = obj["results"]["parameterId"];
Console.WriteLine(token.Path + " -> " + token.ToString());
Console.ReadLine();
What do I need to do in order to return "123.45" using a JPath to "val_1"?
To get the value token from one of the results in the results array based on the value of the parameterId token, you need to use the SelectToken method with a JSONPath query expression:
JToken token = obj.SelectToken("$.results[?(#.parameterId=='val_1')].value");
JSONPath syntax can be a little tricky to get right sometimes, depending on what you're trying to do, so you might find an online expression evaluator such as this one helpful to experiment with.
Alternatively, you can use a LINQ query to do the same thing:
JToken token = obj["results"]
.Where(result => (string)result["parameterId"] == "val_1")
.Select(result => result["value"])
.FirstOrDefault();
Here is a fiddle showing both approaches: https://dotnetfiddle.net/8qiSCa

How to return a subcollection (or object) in json without including all attributes

I am using mongoose as JSON Schema and node.js with it. Need not say, I am new to both. I have been struggling through the day to get this thing work for me but couldn't. Finally, the only solution was to get help from some real nice people out here.
Here is my schema definition -
UserName = {
"properties": {
userURL: {
"description": "URL of this resource",
"type": "string"
},
userName : {
"description": "UserName",
"type": "string",
"required": true
},
}
}
When I make a get call to it, it returns the response in following format -
[
{
"_id": "54c5ede55c82c4bd6abee50a",
"__v": 0,
"properties": {
"userURL": "http://localhost:3000/54c1d6ae441ae900151a6520",
"userName ": "testUser"
}
}
]
Now my requirement is to return the response in following format -
[
{
"_id": "54c5ede55c82c4bd6abee50a",
"userURL": "http://localhost:3000/54c1d6ae441ae900151a6520",
"userName ": "testUser"
}
]
i.e without version and properties tags. I am able to get away with version using following code but properties seems to be tricky thing -
.get(function(request, response) {
UserSchemaModel.find().select('properties.userURL properties.userName').exec (function (err, resObj) {
if (err)
response.send(err);
else{
response.json(resObj);
}
});
});
But it still has properties field :( -
[
{
"_id": "54c5ede55c82c4bd6abee50a",
"properties": {
"userURL": "http://localhost:3000/54c1d6ae441ae900151a6520",
"userName ": "testUser"
}
}
]
I did some google around select as, alias name in select,population in mongoose but no luck.
Kindly suggest. With best Regards.
Just make a new object
response.json(
{
"_id": resObj["_id"],
"userURL": resObj["properties"]["userUrl"],
"userName": resObj["properties"]["userName"]
}
);
Update: Since resObj is an array (as per your comment), you can use Array.prototype.map() to transform them into the right format like so:
response.json( resObj.map(function(o){
return {
"_id": o["_id"],
"userURL": o["properties"]["userUrl"],
"userName": o["properties"]["userName"]
};
})
);
This will return a list of transformed objects that then get passed into the response.json() method.