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.
Hi I have a problem in Vue Js. I want to extract some Json Keys to Data Properties to post into backend using axios.
But when I do it seems failed.
Here is my code:
Method() {
post_tamu(){
this.$store.dispatch("POST_TAMU", {
tamu: {
Nama: this.nama_tamu,
NoIdentitas: this.no_identitas,
NoKontak: this.no_handphone,
Instansi: this.instansi,
fotodiri: this.pict_self,
fotoid: this.pict_id
},
karyawan: {
Nama = this.select_karyawan.Nama,
NoKontak = this.select_karyawan.NoKontak,
},
keperluan: this.activity,
SIK: this.sik,
NDA: this.NDA,
CRQ: this.crq,
status: false
}).then(success => {
this.$router.push('/')
}).catch(error => {
this.error = true;
})
},
}
Computed() {
select_karyawan() {
return this.LIST_KARYAWAN.filter(i => {
return this.karyawan === "" || i.Nama == this.karyawan;
// console.log(karyawan)
});
},
}
I think I did wrong in this.select_karyawan.Nama I dont have any idea to extract keys json value from computed properties.
I need to do a join query on firebase using elasticsearch,
can anyone help me?
In particular I have two nodes, in the child node I have a field that is the id of the father node and I would like to have as a result all the fields of the father node.
How do I build my index in the code?
In adding, in my client I use elasticsearchclient,
here is an extract of the code to index a node:
var db = admin.database();
var etest = db.ref(type);
etest.on('child_added', createOrUpdateIndex);
etest.on('child_changed', createOrUpdateIndex);
etest.on('child_removed', removeIndex);
function createOrUpdateIndex(snap) {
client.index(index, type, snap.val(), snap.key)
.on('data', function(data) { console.log('indexed', snap.key + data ); })
.on('error', function(err) { console.log('index error ', err); }).exec();
}
function removeIndex(snap) {
client.deleteDocument(index, type, snap.key, function(error, data) {
if( error ) console.error('failed to delete', snap.key, error);
else console.log('deleted', snap.key);
});
}
And to take query results:
var queue = db.ref("search");
queue.child('request').on('child_added', processRequest);
function processRequest(snap) {
console.log('process...... ');
snap.ref.remove(); // clear the request after we receive it
var data = snap.val();
// Query ElasticSearch
client.search(index, data.type, { "query": { 'query_string': {
"query" : data.query
}}})
.on('data', function(results) {
var res = JSON.parse(results);
console.log(JSON.stringify(res.hits));
console.log("TOTAL " + JSON.stringify(res.hits.total));
queue.child('response/'+snap.key).set(results);
})
.on('error', function(error){ console.log("errore"); }).exec();
}
Thank you in advance
There are two ways to store your data.
First is creating a nested doc. This is helpful if you need to perform search on some of the values and need other info only for the display.
PUT /my_index {
"mappings": {
"yourIndex": {
"properties": {
"yourColumn": {
"type": "nested",
"properties": {
"name": { "type": "string" },
"parentNode": { "type": "string" },
"childNode": { "type": "string" }
}
}
}
}}}
Eg.
'str1', 'parent1', 'child1'
'str2' 'parent1', 'child2'
If you need not maintain any hierarchy,
you can simply create 3 separate columns and store like a flat data.
You can specify parent1 id and search for all the docs in both the cases.
I did some research on handling POST request with node.js, so that's working now, kind of.
I also know how to save a new JSON object to a mongoDB collection with mongoose.
I feel like I'm close to the solution but I'm having trouble finding the answer.
The specific problem
Now, I need some help to put these two together.
Handling POST request [ok+-] => converting to JSON object/mongoose object [not okay] => saving it to collection [ok+-]
The controllers code
Here is the code I'm using. The basic features work :
handling the fields posted and "saving" them to an object
rendering the fields posted (the "fields" object)
The code:
// controller function for adding a ressource
var post = function(req, res){
// incoming form
var form = new formidable.IncomingForm();
var fields = {};
console.dir('ceci est bien un post');
// everytime an field is parsed...
// This is the place where I should do validation and throw errors if needed
form.on('field', function (field, value) {
console.log(field);
console.log(value);
fields[field] = value;
});
// Once the form is parsed completely
form.on('end', function () {
// testing the output
var ressources = util.inspect(fields, {showHidden: false, depth: null});
console.dir(util.inspect(fields, {showHidden: false, depth: null}));
// here save it in mongodb collection
var ressource = new Ressource(fields);
// ressource.save(function (err, ressource, isSuccess) {
// if (err) {
// res.status(400);
// res.send('Error occured ' + err);
// }
// else if (isSuccess === 1) {
// res.status(201);
// res.send(ressource);
// }
// else {
// res.status(400);
// }
// });
// rendering the page with the results
res.render('addRessourceView', {
title: 'Ressources',
fields: ressources
});
});
form.parse(req);
};
And here's the mongoose model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ressourceModel = new Schema({
title: { type: String },
descr_short: { type: String },
m_visual: { type: Boolean, default: false },
m_writing:{ type: Boolean, default: false },
m_moving: { type: Boolean, default: false },
m_speaking:{ type: Boolean, default: false },
m_music:{ type: Boolean, default: false },
age: String,
minAge: { type: Number },
maxAge: { type: Number },
prerequisite:{ type: Array,
items: { type: String },
uniqueItems: true},
language: { type: String },
source: { type: String },
intra_identify: { type: Boolean, default: false },
intra_expression: { type: Boolean, default: false },
intra_comprehension: { type: Boolean, default: false },
intra_regulation: { type: Boolean, default: false },
intra_utilisation: { type: Boolean, default: false },
theme_impulsivity: { type: Boolean, default: false },
theme_violence: { type: Boolean, default: false },
inter_identify: { type: Boolean, default: false },
inter_expression: { type: Boolean, default: false },
inter_comprehension: { type: Boolean, default: false },
inter_regulation: { type: Boolean, default: false },
inter_utilisation: { type: Boolean, default: false },
details:{
type: Object,
properties: {
goals: { type: Array,
items: { type: String },
uniqueItems: true},
preparation: String,
exercices: { type: Array,
items: { type: Object }},
reflexion: String
}
},
upload:{ type: Array,
items: { type: Object }}
});
module.exports = mongoose.model('Ressource', ressourceModel);
Note : evrything that is "boolean" is a checkbox input
So all these fields are also in the form (I've just not implemented the file upload).
So basically what I need help for is:
adapting the post fields into something I can use with mongoose and save it
maybe some help on how add some validation (like being sure the minAge is smaller than maxAge, or other stuff)
and if you have time or would want to, how to handle file uploads
I'm really here to learn, I'm just a beginner and I tried to be as clear as possible with my question, and I hope you guys will be able to help me out!
Thanks,
Xogno
Yesterday, I spent the whole day working on it and I came to make it work as I wanted ! Actually, after researching a bit more, it wasn't really difficult : it just needed some good logic and taking the time to do it.
Disclaimer : I'm using 'formidable' package to handle the post request, and learned using it with this tutorial.
I first started by adding the structure with comments, just to write down the logical thinking behind my code. And then I started to implement the code for each field one by one. form.on('field'....){} executes each time a field is parsed, so that's how I cand handle them one by one. To do that i used a switch(true) with multiples cases ... : ... break; .
I added the verification needed, adding an error to the object, and this way all the fields were added one by one to an JSON object.
Regarding errors, I displayed them using EJS template in the html page.
Then, once everything was working (handling POST fields and handling errors), the only thing I had left to do was linking it (saving it) to the mongodb collection, using .save method from mongoose.
So this happens all on server side, and doesn't happen dynamically. Maybe later I will recode it to work dynamically, with ajax or some other framework (Vue.js ?), so it doesn't need to reload the whole page and will be more efficient.
Here's the code in case it might help some!
The code handling the post request
form.on('field') // handling each field one by one
form.on('field', function (field, value) {
console.log(field);
console.log(value);
switch (true) {
//check if title is not empty, if empty, throw error
case field === 'title':
if(value == ''){
// then error
console.log('titre vide')
fields.error = ['title-empty'];
fields[field] = value;
}
else{
fields.error = [];
fields[field] = value;
}
break;
// check if description is not empty
case field === 'short_descr':
if(value == ''){
// then error
console.log('titre vide')
fields.error = ['descr-empty'];
fields[field] = value;
}
else{
fields[field] = value;
}
break;
// when receiving minAge or maxAge, save it as number
case (field === 'minAge' || field === 'maxAge') :
if(field === 'maxAge'){
var maxAge = value;
if(Number(maxAge) < Number(fields.minAge)){
// then error
console.log('Error : maxAge plus petit que minAge')
fields.error.push('maxAge<minAge');
}
}
fields[field] = Number(value);
break;
// if 'objectives'
case field === 'objectives':
console.log('hey we have an objectif!')
// when receiving an objective, verify if there's already one
if('objectives' in fields.details){
//then there is already an objective object
console.log('adding an objective to the list');
fields.details[field].push(value);
}
// then we create the objective entry in fields.details
else {
fields.details[field] = [value];
console.log('ajouté aux détails');
}
break;
// if 'materials'
case field === 'materials':
console.log('hey we have an material!')
// when receiving an objective, verify if there's already one
if('materials' in fields.details){
//then there is already an objective object
console.log('adding an material to the list');
fields.details[field].push(value);
}
// then we create the objective entry in fields.details
else {
fields.details[field] = [value];
console.log('ajouté aux détails');
}
break;
// when receiving an exercice, verify if there's already an exercice that exists
// if there's already an exercice that exists, and this one isn't null, add this one as another exercice
// verify also if ex_time is a number
// and insert it under 'details.exercices' (array of objects : {ex_descr: '', ex_time:''})
// renvoyer une erreur si le temps est vide ou si pas un nombre
case field === 'exercices':
console.log('hey we have an exercice!')
// when receiving an objective, verify if there's already one
//first check if it's not empty
if('exercices' in fields.details){
if(value === ''){
//it's empty, add an error
fields.error.push('Empty-' + field + '[' + j + ']');
console.log('exercice is empty');
}
//then there is already an objective object
console.log('adding an exercice to the list');
var object_exercices = {};
object_exercices.ex = value;
fields.details.exercices.push(object_exercices);
j++;
}
// then we create the objective entry in fields.details
else {
if(value === ''){
//it's empty, add an error
fields.error.push('Empty-' + field + '[' + j + ']');
console.log('exercice is empty');
}
var object_exercices = {};
object_exercices.ex = value;
fields.details.exercices = [object_exercices];
console.log('ajouté aux détails');
j++;
}
break;
//if it's an exercice_time we need to add it to the exercice object in the exercice array
case field === 'exercices-time':
console.log('hey we have an exercice-time!')
// when receiving an exercice-time, verify if there's already an exercice existing
if ('exercices' in fields.details){
if(isNaN(value) || value === ''){
// if it's not a number, push a new error
fields.error.push('NaNexercice_time[' + i + ']');
console.log('but its not a number or is empty ' + i);
//recuparate the object of the exercice
var object_exercices = {};
object_exercices = fields.details.exercices[i];
object_exercices.exercice_time = value;
// add the value of ex_time to it
fields.details.exercices[i] = object_exercices;
i++;
} else {
// if it's a number
// and then there is already an exercice object
console.log('adding an exercice-time to the last exercice ' + i);
//recuparate the object of the exercice
var object_exercices = {};
object_exercices = fields.details.exercices[i];
object_exercices.exercice_time = Number(value);
// add the value of ex_time to it
fields.details.exercices[i] = object_exercices;
i++;
}
}
break;
// if "preparation" add it to details.preparation
case field === 'preparation':
fields.details[field] = value;
console.log('ajouté aux détails');
break;
case field === 'intra_identification' || field === 'intra_regulation' || field === 'intra_understanding'
|| field === 'intra_utilisation' || field === 'intra_expression' || field === 'inter_identification'
|| field === 'inter_expression' || field === 'inter_utilisation' || field === 'inter_regulation'
|| field === 'inter_understanding':
//to see if they've at least checked one competence
fields[field] = value;
break;
// otherwise it's a basic input
default:
fields[field] = value;
}
});
form.on('end') // once the whole form is parsed
// Once the form is parsed completely
form.on('end', function () {
// last error handling
//test if they've at least added one competence
if(!fields.intra_identification && !fields.intra_regulation
&& !fields.intra_understanding && !fields.intra_utilisation
&& !fields.intra_expression && !fields.inter_identification
&& !fields.inter_expression && !fields.inter_utilisation
&& !fields.inter_regulation && !fields.inter_understanding) {
fields.error.push('No competence selected');
console.log('No competence selected');
}
// testing the output
console.dir(util.inspect(fields, {showHidden: false, depth: null}));
//time to save
// see if there were errors
if(fields.error.length < 1){
//no errors
//then save it in the database
console.log('no errors');
// save it in mongodb collection
var ressource = new Ressource(fields);
ressource.save(function (err, ressource, isSuccess) {
if (err) {
res.status(400);
//res.send('Error occured ' + err);
res.render('addRessourceView', {
title: 'Ressources',
fields: fields,
saved: false
});
}
else if (isSuccess === 1) {
res.status(201);
//res.send(ressource);
// render the page with save success
res.render('addRessourceView', {
title: 'Ressources',
fields: fields,
saved: true
});
}
else {
res.status(400);
res.render('addRessourceView', {
title: 'Ressources',
fields: fields,
saved: false
});
}
});
} else {
console.dir(fields.error);
// render the page with save success
res.render('addRessourceView', {
title: 'Ressources',
fields: fields,
saved: false
});
}
});
form.parse(req);
I am building a Meteor app and I have Contests/Entries collections. When someone enters the contest, their user_id is pushed into the Contest.entered_users array with $addToSet. Here is the code:
entryInsert: function(entryAttributes) {
check(Meteor.userId(), String);
check(entryAttributes, {
contest_id: String
});
var user = Meteor.user();
var entry = _.extend(entryAttributes, {
user_id: user._id,
user_name: user.profile.name,
submitted: new Date(),
submitted_day: moment().format('MMM D')
});
var currentContest = Contests.findOne(entryAttributes.contest_id);
// Check to make sure that the person has not already entered the giveaway
if (currentContest.entered_users.indexOf(entry.user_id) !== -1) {
throw new Meteor.Error('invalid', "You have already entered the giveaway");
} else {
Contests.update(
currentContest._id,
{
$addToSet: {entered_users: entry.user_id},
$inc: {entries: 1}}
);
}
// Create entry in order to get the entry id
var entryId = Entries.insert(entry, function(err) {
if (err) {
alert(err.reason);
}
});
return {
_id: entryId
}
}
I want to remove a persons user_id from the Contest.entered_users array when an entry is removed. I am trying to use $pull but it doesn't appear to be working... When I remove an entry, the entry.user_id is still in the contest.entered_users array. Here is the relevant code:
'click .entry-delete': function(e, tmpl) {
e.preventDefault();
var currentEntry = this;
var currentEntryId = this._id;
var contestId = Contests.findOne(currentEntry.contest_id);
// Update the contest by removing the entry's useer_id from entered_users
Meteor.call('contestRemoveEntry', contestId, currentEntry, function(error) {
if (error) {
alert(error.reason);
}
});
Meteor.call('entryRemove', currentEntryId, function(error) {
if(error) {
alert(error.reason);
}
});
}
Here is the contestRemoveEntry method:
contestRemoveEntry: function(contestId, currentEntry) {
Contests.update({ _id: contestId }, { $pull: { entered_users: currentEntry.user_id } } );
}
Any ideas as to why this is not working? I've tried other SO solutions but nothing seems to be working.
It appears that this is the correct way to make $pull work:
Contests.update(contestId, { $pull: { entered_users: currentEntry.user_id } } );