How to validate a JSON request based on a schema using Javascript - javascript

I am trying to build out an API endpoint within Workato using a Javascript plugin. Part of the requirement is to validate if the JSON request is valid based on the schema that I defined.
Specifically, I need to throw a 400 Bad Request error if a required field is not provided or if the provided value does not match the expected data type (e.g. string when expecting number).
My original thought was to define a "checkRequest" function that will intake the JSON request object with every possible data point and essentially run through a huge list of if/else if /else conditions to test every value for "null" and use "type of" to test for data type and then return if I encounter an error.
However, note that the schema is HUGE with about 100 different data points and consists of nested objects within objects so I was hoping to find the most optimized way to iterate rather than relying on a bulk of if statements.
See my code below and a snippet of a request based on the schema (can't share full schema):
My code:
function checkRequest({
partner_id,
merchant_reference_id,
optional_string,
}) {
let response = {
validRequest: true,
responseStatus: "",
errorTitle: "",
errorDetail: "",
errorCode: "",
};
let badRequestTitle = "Bad Request";
let requiredFieldErrorDetail = (field) => {
return field + " is a required field but was not provided in request.";
};
let wrongFormatErrorDetailDetail = (field, inputDataType, expectedDataType) =>
field +
" expected data type is " +
expectedDataType +
", but " +
inputDataType +
" was received.";
//partner_id
if (partner_id == "" || partner_id == null) {
response.validRequest = false;
response.errorDetail = requiredFieldErrorDetail("partnerID");
console.log(response);
return response;
} else if (typeof partner_id != "string") {
response.validRequest = false;
response.responseStatus = "400";
response.errorCode = "40001";
response.errorTitle = badRequestTitle;
response.errorDetail = wrongFormatErrorDetail(
"partnerID",
typeof partner_id,
"string"
);
console.log(response);
return response;
}
//merchant_reference_ID
else if (merchant_reference_id == "" || merchant_reference_id == null) {
response.validRequest = false;
response.errorDetail = requiredFieldErrorDetail("partnerID");
console.log(response);
return response;
} else if (typeof merchant_reference_id != "string") {
response.validRequest = false;
response.responseStatus = "400";
response.errorCode = "40001";
response.errorTitle = badRequestTitle;
response.errorDetail = wrongFormatErrorDetail(
"partnerID",
typeof merchant_reference_id,
"string"
);
console.log(response);
return response;
}
//else
else {
console.log(response);
return response;
}
}
let requestBody = {
partner_id: "",
merchant_reference_id: "aa",
optional_string: "3",
};
checkRequest(requestBody);
Sample request snippet (assume that some fields are required and others are optional):
{
"application": {
"partnerReferral": {
"partnerID": "mg3e09f8-a8dd-44e6-bb06-55293b799318",
"merchantReferenceID": "mg3e09f8a8dd44e6bb06-55293b799318"
},
"businessInformation": {
"identity": [
{
"identifier": "EMPLOYER_IDENTIFICATION_NUMBER",
"value": "77-1122333"
}
],
"businessType": "ASSOCIATION",
"classification": {
"code": "SIC",
"value": "string"
}
}
}
}
Hoping to rely on vanilla Javascript to accomplish this is the best way possible, but open to plugins if necessary.

Related

Combining multiple objects to create JSON object in Node

I am trying to construct my own JSON object from multiple online image/photography sources. The below code should explain what I am trying to accomplish:
var searchUnsplash = require('./apis/unsplash');
var searchFlickr = require('./apis/flickr');
function combineObjs(callback) {
var obj = {}
var key = 'item';
obj[key] = [];
searchFlickr.searchFlickr(searchTerm, searchCount, searchPage,
function (callback) { // each API call is in a separate file with these functions exported
obj[key].push(flickrObj); // this does not work
// console.log(flickrObj) // this works
});
searchUnsplash.searchUnsplash(searchTerm, searchCount, searchPage,
function (callback) {
obj[key].push(unsplashObj);
// console.log(unsplashObj)
});
console.log(obj)
}
combineObjs();
The goal is to end up with a JSON object like below:
["item": {
"id": 1,
"title": 2,
"content": 3,
"source": "flickr"
},
"item": {
"id": 1,
"title": 2,
"content": 3,
"source": "unsplash"
}]
etc, which I can use to power my front end.
I am a beginner to javascript and I am just working off what I have learned in tutorials and articles, so I might be using the wrong approach entirely for what I am aiming to achieve. Happy to take any pointers
search function:
function searchUnsplash(term, count, page, callback) {
request(`https://api.unsplash.com/search/photos/?per_page=${count}&page=${page}&query="${term}"&client_id=KEY&`,
function searchResult(error, response, body) {
if (!error && response.statusCode == 200) {
var info = JSON.parse(body)
for ( var i = 0; i < info.results.length; i++) {
obj = {
id: `us-${info.results[i].id}`,
}
callback(obj);
}
}
})
}
module.exports.searchUnsplash = searchUnsplash;
First, your intended result is not correct. You can't name "item" the individual array entries. A corrected and working example would be this one.
[ {
"id": 1,
"title": 2,
"content": 3,
"source": "flickr"
},
{
"id": 1,
"title": 2,
"content": 3,
"source": "unsplash"
}]
Second, you mistake JSON for your data structure. JSON is just the text notation. So, let's see first how to build a suitable data array.
let results = [];
results.push( { id:1, title:2, content:3, source:"flickr" });
results.push( { id:2, title:4, content:6, source:"unsplash" });
And then with JSON.stringify(results) will code your results into JSON.
Finally, you mix up the aynchronous calls in your code with synchronous invocations. You need to save the results on the callback of the individual functions, that is when you really obtain the responses asynchronously. Also, you need to count the pending results and invoke the final callback when all done.
So, putting all the pieces together, in a contrived fake example, we just invoke twice the search functions and so we callback when two results are combined.
function combineObjs(callback) {
let results = [];
function partialResult(obj) {
results.push(obj);
if (results.length=2) callback(results);
};
searchFlickr(searchTerm, searchCount, searchPage, partialResult);
searchUnsplash(searchTerm, searchCount, searchPage,partialResult);
}
combineObjs( function(results) { console.log(JSON.stringify(results)) });
This is excessive but it would work. It can be used over and over and over again. :D
Run the snippet to see a result
class JSONBuilder
{
constructor(contents=null)
{
if(!contents)
{
//Private objecy hash that isn't publicly accessible
var objectHash = {};
//Get stashed item
this.GetItem = function(key)
{
if(!key) throw new Error("Null or Underfined Key Passed.");
let value = objectHash[key];
if(!value) throw new Error(`Key : ${key} Not Found in JSON objectHash`);
return value;
}
//Set an item in the objecy hash
this.SetItem = function(key, value)
{
if(!key) throw new Error("Null or Underfined Key Passed.");
if(!value) throw new Error("Null or Underfined Key Not Found.");
objectHash[key] = value;
}
//Remove item from the hash
this.DeleteItem = function(key)
{
if(!key) throw new Error("Null or Underfined Key Passed.");
if(!objectHash[key]) throw new Error(`Key : ${key} Not Found in JSON objectHash`);
objectHash.DeleteItem(key);
}
//Turn items into a JSON object
this.Build = function()
{
return JSON.stringify(objectHash);
}
}
else
{
//If a string is passed as a paremeter, reconstruct from that
try
{
objectHash = JSON.parse(contents);
}
catch(Err)
{
console.log("Parsing of JSON Content Failed.");
throw Err;
}
}
}
}
class Item
{
constructor(id, source, content, title)
{
this.Id = id;
this.Source = source;
this.Content = content;
this.Title = title;
}
}
let builder = new JSONBuilder();
let itemContainer = [];
itemContainer.push(new Item(1, 'flicker', 'stuff', 'item1'));
itemContainer.push(new Item(2, 'flicker', 'morestuff', 'item2'));
builder.SetItem('items', itemContainer);
console.log(builder.Build());

Attribute may not contain an empty string

I am parsing a CSV file and putting the data in a table with AWS DynamoDB.
As it stands right now, I am getting the following error:
One or more parameter values were invalid: An AttributeValue may not contain an empty string
... BEFORE it puts the data in the table. The data is getting to the table, but not before spamming me with that error a million times.
My Code:
var csv = require("fast-csv");
csv.fromPath(file, {
headers: true,
ignoreEmpty: true
})
.on("data", function(data) {
for (var key in data) {
if (data.hasOwnProperty(key)) {
if (data[key] === "" || data[key] === undefined || data[key] === null) {
data[key] = "N/A";
}
}
params = {
TableName: tableName,
Item: {
RefID: {
S: data["Ref-ID"]
},
//lots of other data
}
};
dynamodb.putItem(params, function(err, data) {
if (err) {
console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
}
else {
console.log("Added item:", JSON.stringify(data, null, 2));
}
});
}
})
.on("end", function() {
console.log("done");
});
As you can see, I am converting any possible empty strings to == N/A in an attempt to solve this problem. Any thoughts?
EDIT:
This turns out to be undefined when it should display what it put in the table.
console.log("Added item:", JSON.stringify(data[key], null, 2));
EDIT 2: Changed this code...
dynamodb.putItem(params, function(err, data)
...to this:
dynamodb.putItem(params, function(err, info)
I am still getting the errors, but am now displaying the table correctly.
It appears that dynamoDB at this time does not allow empty strings. I can NOT understand why, but as of this date you cannot not store an attribute of "Key":"".
Please complain to amazon about it. key="" and key=null are very different use cases and are needed.
Try doing field validation on your param.Item Object to verify that everything is set properly; and find the errornous fields that are plaguing your console.
var tableName = "wombat_habitats";
var data = {
"Ref-ID": "Charlie"
};
params = {
TableName: tableName,
Item: {
RefID: {
S: data["Ref-ID"]
},
SomethingElse: {
S: data["Bad-Key"]
}
//lots of other data
}
};
for(var itemKey in params.Item) {
for(var itemAttr in params.Item[itemKey]) {
var value = params.Item[itemKey][itemAttr];
if(value === undefined || value === "") {
console.log("item", itemKey, "of type", itemAttr, "is undefined!")
}
}
}

Javascript associate dynamic array

I have a JSON response as below;
"finalData" :
[
{
"message":"Display success msg",
"status":["SUCCESS"]
},
{
"message":"Display fail msg",
"status":["FAIL"]
}
]
Now this is dynamic. Meaning, I can either get just "SUCCESS" or just "FAILURE" or both
So finalData can be an array of 0 or 1 or 2 objects
My question is what is the best way to access the "message" property of the array dynamically. i.e. I want to know if finalData.message belongs to
"status":["SUCCESS"] or "status":["FAILURE"]
So kind of associate the array
var d = { "finalData": [{ "message": "Display success msg", "status": ["SUCCESS"] }, { "message": "Display fail msg", "status": ["FAIL"] }] }
var status = 'SUCCESS';
var message = d.finalData.filter(e => e.status == status).map(e => e.message)[0];
document.write(message);
ES5 code:
var d = { "finalData": [{ "message": "Display success msg", "status": ["SUCCESS"] }, { "message": "Display fail msg", "status": ["FAIL"] }] }
var status = 'SUCCESS';
var message = d.finalData.filter(function(e) {
return e.status == status;
}).map(function(e) {
return e.message;
})[0];
document.write(message);
Check whether each code is there, then just use an if statement to continue conditionally, depending on the status.
var status = finalData[0].status;
var succeeded = status.indexOf('SUCCESS') >= 0;
var failed = status.indexOf('FAIL') >= 0;
if(succeeded && failed) {
// code when both are true
}
else if(succeeded) {
// code for only success
}
else if(failed) {
// code for only failure
}
else {
// code if neither are present
}

Ajax in spring return java.lang.stackoverflow error

Controller returns Java.lang.stackoverflow error when calling from ajax.
My Ajax function is like this.
$.ajax({
url: '${pageContext.servletContext.contextPath}/exam/test',
type: 'POST',
data: 'examName='+examName,
success: function(response) {
alert(response);
}
});
Controller
#RequestMapping(value = "/exam/test", method = RequestMethod.POST)
public #ResponseBody List<SchoolExam> examsTest(#ModelAttribute(value = "examName") String examName, BindingResult result, WebRequest webRequest, ModelMap map, Principal principal) {
User loggedUser = userService.getUserByUserName(principal.getName());
***********************
Some code here
***********************
List<SchoolExam> schoolExams = new ArrayList<SchoolExam>();
for (School school : schools) {
if (student) {
Set<Student> students = school.getStudents();
for(Student std : students) {
if (std != null && !std.isEmpty()) {
schoolExams.add(new SchoolExam(std, true));
}
}
}
if (teacher) {
Set<Teacher> teachers = school.getEvents();
for (Teacher tchr : teachers) {
if (loggedUser.equals(tchr.getOwner())) {
schoolExams.add(new SchoolExam(tchr, true));
}
}
}
if (exam) {
Set<Exam> exams = school.getCampaigns();
for (Exam exam1 : exams) {
if (loggedUser.equals(exam1.getOwner())) {
schoolExams.add(new SchoolExam(exam1, true));
}
}
}
}
return schoolExams;
}
SchoolExam
public SchoolExam(Object obj, boolean editable) {
this.editable = editable;
if (obj instanceof Student) {
Student student = (Student) obj;
this.id = student.getId();
this.name = student.getName();
this.type = Constants.Student;
this.obj = student; // <-- This is causing issue here
}
if (obj instanceof Teacher) {
Teacher teacher = (Teacher) obj;
this.id = teacher.getId();
this.name = teacher.getName();
this.type = Constants.Teacher;
this.obj = teacher; // <-- This is causing issue here
}
if (obj instanceof Exam) {
Exam exam = (Exam) obj;
this.id = exam.getId();
this.name = exam.getName();
this.type = Constants.Exam;
this.obj = exam; // <-- This is causing issue here
}
}
Issue:
This is working fine when a form is submit then I can use all data by running foreach loop in jsp but when I tried to return list in my function then ajax work successfully and it also return me response
response in ajax
[
{
"id":"2123244",
"name":"UK School",
"type":"exam",
"editable":true,
"obj":
{
"id":"2123244",
"authorizationRequired":false,
"owner":
{
"id":"5676764554",
"company":
{
"id":"55435435345",
"name":"SchoolTest Software",
"enabled":true,
"size":3,
"sector":null,
"phone":"1231231232",
"schoolFees":5000,
"location":"US",
"users":
[
{
"id":"5676764554",
"company": // <-- Start Repeating here
{
"id":"55435435345",
"name":"SchoolTest Software",
"enabled":true,
"size":3,
"sector":null,
"phone":"1231231232",
"schoolFees":5000,
"location":"US",
"users":
[
{
"id":"5676764554",
"company": // <-- Repeating again
{
"id":"55435435345",
"name":"SchoolTest Software",
"enabled":true,
"size":3,
"sector":null,
"phone":"1231231232",
"schoolFees":5000,
"location":"US",
"users":
[
{
"id":"5676764554",
"company":// <-- It keeps repeating it self
but when I tried to print list value in controller it's only printing one value.
e.g:
for (SchoolExam schoolExam : schoolExams) {
System.out.println("Name: " + schoolExam.getName());
System.out.println("ID: " + schoolExam.getId());
Exam exam = (Exam) schoolExam.getObj();
System.out.println("Exam Name: " + exam.getName());
}
Output:
Name: UK School
ID: 2123244
Exam Name: UK School
Note:
If I comment obj line then everything works fine for me.
e.g:
this.obj = student;
this.obj = teacher;
this.obj = exam;
But I need to use this to get data as It contain data for different table.
Please find this file for error log that I'm getting in console.
So, What I'm doing wrong which cause this issue or I need to use any other way to prevent this issue.
Any suggestion or link will be helpful.
According to your error log file, you have a cyclic recursion in your Java classes. So Jackson try to serialize some objects infinitely.
The main problem is here : com.school.model.Company_$$_javassist_8["users"]->org.hibernate.collection.PersistentSet[0]->com.school.model.User["company"]->com.school.model.Company_$$_javassist_8["users"]
You have a property users containing a set of User containing a property ompany containr a Company containing the same users. So infinity loop.

Reading the JSON values using Javascript/Angular

Hi i am getting the following response from server
{
"Application1": {
"status": "SUCCESS",
"comment": "A123456 added successfully to Application"
},
"Application2": {
"status": "SUCCESS",
"comment": "B67890 added successfully to Application"
}
}
i need to show a message based on the status , we are using angular and javascript i am unable to loop through and read the same, any help would be appreciated
The simpliest version i can imagine:
<script>
var json = {"Application1":{"status":"SUCCESS","comment":"A123456 added successfully to Application"},"Application2":{"status":"SUCCESS","comment":"B67890 added successfully to Application"}};
for(var t in json)
console.log(json[t]['status']);
</script>
You can read the values by parsing the string as json:
var obj = JSON.parse('{"Application1":{"status":"SUCCESS","comment":"A123456 added successfully to Application"},"Application2":{"status":"SUCCESS","comment":"B67890 added successfully to Application"}}')
Then you can get access the values as properties:
obj.Application1.status
First check the response is JSON object or string. If it is string, parse it to JSON. Then you can loop through it.
Use the following functions
isJson(your_response);
will return the JSON object or null.
var whatIsIt = function (object) {
var stringConstructor = "test".constructor;
var arrayConstructor = [].constructor;
var objectConstructor = {}.constructor;
if (object === null) {
return "null";
}
else if (object === undefined) {
return "undefined";
}
else if (object.constructor === stringConstructor) {
return "String";
}
else if (object.constructor === arrayConstructor) {
return "Array";
}
else if (object.constructor === objectConstructor) {
return "Object";
}
else {
return "don't know";
}
};
var isJson = function(o1)
{
// Validate JSON strings
var json_object = null;
var type = whatIsIt(o1);
switch(type)
{
case "String":
try{
json_object = JSON.parse(o1);
}catch (e){
return null;
}
break;
case "Object":
try {
JSON.stringify(o1);
json_object = o1;
}catch (e) {
return null;
}
break;
}
return json_object;
};
Assuming that the communication with the server is carried out in a separate angular service, you need to use ng-repeat and ng-if directives,
Please find the JSFiddle here :
http://jsfiddle.net/2ju3c6tc/1/
var module = angular.module("myModule", []);
module.service('serverCommunicator', ['$http',
function($http) {
//code to get the data from the server
//server Data would hold the JSON object after the AJAX req,however, it is assigned manually over here
this.serverData = {
"Application1": {
"status": "SUCCESS",
"comment": "A123456 added successfully to Application"
},
"Application2": {
"status": "SUCCESS",
"comment": "B67890 added successfully to Application"
}
};
}
]);
module.controller('myController', ['$scope', 'serverCommunicator',
function($scope, serverCommunicator) {
$scope.serverData = serverCommunicator.serverData;
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="myModule">
<div ng-controller="myController as myCont">
<div ng-repeat="dataElement in serverData">
<div ng-if="dataElement.status=='SUCCESS'">
This is success message :{{dataElement.comment}}
</div>
<div ng-if="dataElement.status=='FAILURE'">
This is failure message {{dataElement.comment}}
</div>
</div>
</div>
</div>

Categories