Displaying Toaster Errors for Bad Http Requests - json

I am trying to catch an error thrown by my Web API in Angular, and I want to display a user-friendly error message in certain cases. How would I access the string "PE and Owner Signature must be attached for a status of Submitted", given the response body below?
{
"data": {
"model.WorkflowStepId": [
"PE and Owner Signature must be attached for a status of Submitted"
]
},
"exceptionType": "FieldValidation"
}
This is what I have so far, but I'm stuck since I am currently only displaying the string "model.WorkflowSetId".
this.spinner = this.certService.updateCert(this.damId, this.certId, this.model)
.subscribe(response => {
...
},
(errorRes: HttpErrorResponse) => {
if (errorRes.error && errorRes.error.exceptionType === 'FieldValidation') {
const errors = errorRes.error.data;
for(let error in errors)
this.notificationService.error(error);
} else {
console.log(errorRes);
this.notificationService.error('An unknown error has occurred. Please try again.');
}
});

You may simply do:
if (errorRes.error && errorRes.error.exceptionType === 'FieldValidation') {
this.notificationService.error(errorRes.error.data.model.WorkflowStepId[0]);
}

So it turns out that "model.WorkflowStepId" was actually a string. To capture it and other types of validation errors I was able to loop through bad requests, build a string that grouped the same types of field validation errors into single messages, and display those messages to the user using the toaster.
if (errorRes.error && errorRes.error.exceptionType === 'FieldValidation') {
for (var key in errorRes.error.data) {
for (var i = 0; i < errorRes.error.data[key].length; i++) {
errorStr += (errorRes.error.data[key][i]);
errorStr += ". ";
}
this.notificationService.error(errorStr);
}
}

Related

Unable to parse JSON data due to errors or undefined data

I apologize if this seems similar to other questions asked but I have not been able to find any posts that have resolved this issue for me. Basically, I am getting a JSON object and I am trying to parse it but I can't parse it correctly. Mainly the WordDetails section that I am getting from a Word API. I am able to get everything outside the results section under WordDetails. Basically, when I get to results, I am not able to parse it correctly. Below is an example of the format.
{
"LastIndex": 133,
"SRDWords": [
{
"Domain": {
"URL": "abactinal.com",
"Available": true
},
"WordDetails": "{\"word\":\"abactinal\",\"results\":[{\"definition\":\"(of radiate animals) located on the surface or end opposite to that on which the mouth is situated\",\"partOfSpeech\":null,\"antonyms\":[\"actinal\"]}],\"syllables\":{\"count\":4,\"list\":[\"ab\",\"ac\",\"ti\",\"nal\"]}}"
},
{
"Domain": {
"URL": "aaronical.com",
"Available": true
},
"WordDetails": "{\"word\":\"aaronical\",\"syllables\":{\"count\":4,\"list\":[\"aa\",\"ron\",\"i\",\"cal\"]},\"pronunciation\":{\"all\":\"ɜ'rɑnɪkəl\"}}"
},
...
Here is my code below. Basically, I am getting to the results section of WordDetails but if I try to parse the results section it fails and if I try object.entries on it, it will not return a response according to the alert messages I used. I know there must be a better way but not sure what. Most articles say just JSON.parse then map it but that does not work. Any help would be appreciated!
data.Words.map(word => {
//get data
for (let [key, value] of Object.entries(word)) {
if (key === "Domain") {
url = value.URL;
availability = value.Available;
} else if (key.trim() === "WordDetails") {
alert("value " + value);
wDetails = JSON.parse(value);
for (let [key2, value2] of Object.entries(wDetails)) {
if (key2 === "word") {
//store word
} else if (key2.toString().trim() === "results") {
let test = JSON.parse(value2);
test = Object.entries(value2);
test.map(t => {
alert(t.definition);
});
}
}
}
}
});
You did JSON.parse above, no need to parse value2 again.
And value for results is an array, so no need for Object.entries.
...
} else if (key2.toString().trim() === 'results') {
let test = JSON.parse(value2); // this should be remove
test = Object.entries(value2); // this should be remove, value2 should be an array
// map value2 directly
value2.map(t => {
alert(t.definition);
});
}
...

Postman test for empty dictionary value of json name

How can I write a test for an empty value of a specific JSON name pair. For example I have this JSON:
{
"data": {
"sectionGroupName": "PPConfig:APIMethod",
"sections": {}
},
"success": true,
"errorMessage": ""
}
I want to check if sections is empty, like it is in this case. I have other successful tests written like this:
tests["Status code is 200"] = responseCode.code === 200;
var body = JSON.parse(responseBody);
tests["Success Response"] = body.success === true;
tests["No Error message"] = body.errorMessage === "";
tests.Data = body.data.sectionGroupName === "PPConfig:APIMethod";
But I haven't been able to find successful test code for checking if the value of a specific name is an empty dictionary. Can someone help me with this as an example please?
You can get the list of properties of sections and test its length.
let sectionKeys = Object.keys(body.data.sectionGroupName)
if(sectionKeys.length){
//Proceed with section
} else {
//Proceed when it's empty
}
See Object.keys()
from this link
to check if it's a dictionary (use your 'sections' as v)
function isDict(v) {
return !!v && typeof v==='object' && v!==null && !(v instanceof Array) && !(v instanceof Date) && isJsonable(v);
}
Then check that it is empty (from this other link) use:
function isEmpty(obj) {
for (var x in obj) { return false; }
return true;
}
That should work

SyntaxError: Unexpected token u in JSON at position 0 in Angular2 when try to store in local storage

I have a json response like this.
{
"response": {
"data": {
"name": "ABC",
"Id": "1234",
"address": "adthhyrr"
}
}
}
In service.ts I am doing a request like this to get the response data.
public getResponseData() : Promise<any> {
if(typeof(this.login_data) === "undefined") {
return this.http.get('assets/response.json')
.toPromise().then(res => {
this.login_data = res.json().response.data;
return this.login_data;
}).catch(this.handleError);
} else {
return Promise.resolve(this.login_data);
}
}
And I am storing the login_data in localstorage like this
getAllData() {
if (localStorage.getItem('login_data') === null || localStorage.getItem('login_data') === undefined) {
localStorage.setItem('login_data', JSON.stringify(this.login_data));
return this.login_data;
}
else {
var login_data = JSON.parse(localStorage.getItem('login_data'));
return login_data;
}
}
And in component.ts class I am getting data like this
this.logindata = this.myservice.getAllData();
But while doing this I am getting this error.
SyntaxError: Unexpected token u in JSON at position 0.
But if i am not storing in localstorage then i am doing like this in service.
public getAllData(): any {
return this.login_data;
}
And in component.ts file i am getting data by calling this method.
this.logindata = this.myservice.getAllData();
And here it is working fine.But when i am trying to store data into localstorage and want to fetch from local storage i am getting this error.
SyntaxError: Unexpected token u in JSON at position 0
Can anyone please tell me where exactly i am doing wrong.
The error SyntaxError: Unexpected token u in JSON at position 0 says that you are trying to parse an object to object which is not possible.
So, while retrieving from localStorage check the type and parse if it is not an object
Also change the if condition to,
if ( !(localStorage.getItem('login_data'))) {
getAllData() {
var data = localStorage.getItem('login_data');
if (!data) {
localStorage.setItem('login_data', JSON.stringify(this.login_data));
return this.login_data;
} else {
var login_data;
if(typeof data == 'object')
{
login_data = data;
}else{
login_data = JSON.parse(data);
}
return login_data;
}
}

Maintaining session in Gupshup bot calls to Api.ai

I am building a bot in Gupshup with Api.ai integration. I have an agent in Api.ai with several intents and each of them linked through contexts(input & output contexts). When I use the following code to call Api.ai, the first intent is called and I get the reply. However when the second message is given, the bot takes it as a completely new message, without identifying its relation with first.
How can I solve this issue? Kindly help
function MessageHandler(context, event) {
// var nlpToken = "xxxxxxxxxxxxxxxxxxxxxxx";//Your API.ai token
// context.sendResponse(JSON.stringify(event));
sendMessageToApiAi({
message : event.message,
sessionId : new Date().getTime() +'api',
nlpToken : "3626fe2d46b64cf8a9c0d3bee99a9sb3",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.parse(res).result.fulfillment.speech);
}
},context)
}
function sendMessageToApiAi(options,botcontext) {
var message = options.message; // Mandatory
var sessionId = options.sessionId || ""; // optinal
var callback = options.callback;
if (!(callback && typeof callback == 'function')) {
return botcontext.sendResponse("ERROR : type of options.callback should be function and its Mandatory");
}
var nlpToken = options.nlpToken;
if (!nlpToken) {
if (!botcontext.simpledb.botleveldata.config || !botcontext.simpledb.botleveldata.config.nlpToken) {
return botcontext.sendResponse("ERROR : token not set. Please set Api.ai Token to options.nlpToken or context.simpledb.botleveldata.config.nlpToken");
} else {
nlpToken = botcontext.simpledb.botleveldata.config.nlpToken;
}
}
var query = '?v=20150910&query='+ encodeURIComponent(message) +'&sessionId='+sessionId+'&timezone=Asia/Calcutta&lang=en '
var apiurl = "https://api.api.ai/api/query"+query;
var headers = { "Authorization": "Bearer " + nlpToken};
botcontext.simplehttp.makeGet(apiurl, headers, function(context, event) {
if (event.getresp) {
callback(event.getresp);
} else {
callback({})
}
});
}
/** Functions declared below are required **/
function EventHandler(context, event) {
if (!context.simpledb.botleveldata.numinstance)
context.simpledb.botleveldata.numinstance = 0;
numinstances = parseInt(context.simpledb.botleveldata.numinstance) + 1;
context.simpledb.botleveldata.numinstance = numinstances;
context.sendResponse("Thanks for adding me. You are:" + numinstances);
}
function HttpResponseHandler(context, event) {
// if(event.geturl === "http://ip-api.com/json")
context.sendResponse(event.getresp);
}
function DbGetHandler(context, event) {
context.sendResponse("testdbput keyword was last get by:" + event.dbval);
}
function DbPutHandler(context, event) {
context.sendResponse("testdbput keyword was last put by:" + event.dbval);
}
The sessionId has to be fixed for a user. There are two ways you can do this in the Gupshup bot code -
Use the unique userID which is sent to the bot for every user.
To get this value you can use -
event.senderobj.channelid
But this value is dependent on how different messaging channels provides it and api.ai has a limit of 36 characters.
Sample code -
function MessageHandler(context, event) {
sendMessageToApiAi({
message : event.message,
sessionId : event.senderobj.channelid,
nlpToken : "3626fe2d46b64cf8a9c0d3bee99a9sb3",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.parse(res).result.fulfillment.speech);
}
},context)
}
Generate a unique sessionId for each user and store it in the database to utilise it. In the below sample , I am storing the sessionId at roomleveldata which is the default persistance provided by Gupshup, to know more check this guide.
Sample code -
function MessageHandler(context, event) {
sendMessageToApiAi({
message : event.message,
sessionId : sessionId(context),
nlpToken : "84c813598fb34dc5b1f3e1c695e49811",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.stringify(res));
}
},context)
}
function sessionId(context){
var userSession = context.simpledb.roomleveldata.sessionID;
if(!userSession){
userSession = new Date().getTime() +'api';
context.simpledb.roomleveldata.sessionID = userSession;
return userSession;
}else{
return userSession;
}
}
Remember that sessionId should not exceed 36 characters.
Suresh,
It seems you generate new session id for every request:
new Date().getTime() +'api'
But if you want to make contexts work it must be one fixed value for all requests belonging to one user. For example, you could use some global variable for it.

Elmah and DbEntityValidationException

I have setup a project with both Elmah and EF4.1 Code First.
The project is throwing a System.Data.Entity.Validation.DbEntityValidationException, but Elmah is not providing enough detail to determine what validation is failing. All that is logged is:
System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
Is there a way to make Elmah expand and log the EntityValidationErrors property?
List<IUserFeedback> errors = new List<IUserFeedback>();
try
{
_dbContext.SaveChanges();
Updated(this, HasUnsavedChanges);
}
catch (DbEntityValidationException ex)
{
foreach (var x in ex.EntityValidationErrors)
{
foreach (var y in x.ValidationErrors)
{
if (!String.IsNullOrWhiteSpace(y.PropertyName))
errors.Add(new UserFeedback() {
FeedbackFlags = TypeOfUserFeedbackFlags.Error,
Message = String.Format("Unable to save {0} due to an issue with its \"{1}\" value. The error returned was \"{2}\"",x.Entry.Entity, y.PropertyName, y.ErrorMessage)
});
else
errors.Add(new UserFeedback() {
FeedbackFlags = TypeOfUserFeedbackFlags.Error,
Message = String.Format("Unable to save {0} due to the error \"{1}\"", x.Entry, y.ErrorMessage)
});
}
}
}
return errors;