Duck Typing with JSON object - try/except? - javascript

In AngularJS, I have a api request that goes out and a JSON is returned. The JSON is stored as object data and I use data.Automation.Status to check the string in Status.
There are a few JSON errors that could a rise(after http 200 success with the json returned successfully):
The entire JSON could be returned a blank string ""
data JSON object could exist, but Automation JSON object could be undefined
Property Status of object Automation could be undefined or a blank string
Coming from python, all these possible situations could easily be handled in a try/except block.
Try:
do something with JSON
except (blah, blah)
don't error out because JSON object is broken, but do this instead
I see angular has $errorHandler service that can be modified with custom handlers. But I am not sure if this can be used in the same manner of duck typing that I am looking for.
How could I go about duck typing in AngularJS? Specifically, for the JSON object error scenerios mentioned in the list above?
How I am using data.Automation.Status at the moment:
if (iteration < Configuration.CHECK_ITERATIONS && data.Automation.Status !== "FAILED") {
iteration++;
return $timeout((function() {
return newStatusEvent(eventId, url, deferred, iteration);
}), Configuration.TIME_ITERATION);
} else {
err = data.Automation.StatusDescription;
return deferred.reject(err);
}

Here's how I came to a solution for the same problem.
It keeps it minimal, and all of the tests are grouped in one block.
$http.get('/endpoint1').success(function(res) {
try {
// test response before proceeding
if (JSON.stringify(res).length === 0) throw 'Empty JSON Response!';
if (!res.hasOwnProperty('Automation')) throw 'Automation object is undefined!';
if (!res.Automation.hasOwnProperty('Status') || res.Automation.Status !== 'SUCCESS')
throw 'Automation Status Failed!';
} catch (err) {
// handle your error here
console.error('Error in response from endpoint1: ' + err);
}
// if your assertions are correct you can continue your program
});

The best way that comes to mind to handle this situation would be using $parse since it handles undefined very well. You could do something like this:
$http.get('/endpoint1').success(function(res) {
try {
// test response before proceeding
if (angular.isUndefined($parse('Automation.Status')(res))) {
throw 'Automation Status Failed!';
}
} catch (err) {
// handle your error here
console.error('Error in response from endpoint1: ' + err);
}
// if your assertions are correct you can continue your program
});
You can see in this plunker how $parse handles your scenarios.

Depending on how complicated you want to get. If you want to use coffeescript, there's a nice ? operator: json.knownToExist.maybeExists?.maybeAlsoExists?() will never error out, and will only call maybeAlsoExists if it exists.
Otherwise, you could use typeof checks:
foo = {a: 1, b: 2}
typeof foo === 'object'
typeof foo.bar === 'undefined'
I'm under the impression that try/catch blocks in javascript are relatively expensive. So you probably want to test the json manually, especially if the different return values from your service are what you'd call 'normal' responses. Exceptions, IMO, should be used for exceptional occurrences, and not sanity checking.

Related

How to create a conditional based on a response object, for which the Key values are dependent and avoiding object.key() is Undefined

I'm subscribing to a method in Angular using typescript, which is a simple post Call that returns an entire HTTP Response. I'm wanting to create a simple conditional that checks to see if a certain key exists in the HTTP response. The conditional will work with an object that contains the key, but for an object that doesn't contain the key it throws an undefined.
Is there a way to omit the error or exception thrown and to proceed to the else portion
The keys of the object body are added if the user includes a number, without that it doesn't exist with the PhoneNumber key value
what I've tried / flipped the if and else
!= undefined (vice versa)
== null (vice versa)
this.postMyName(userInfo).subscribe(
(result:Response) => {
if (result['body']['user'].hasOwnProperty(['PhoneNumber'])) {
console.log("User has included a phoneNumber");
} else {
console.log("User has Not included a phoneNumber");
}
},error ...)
You can also try this solution maybe it will work
this.postMyName(userInfo).subscribe(
(result:Response) => {
if ('PhoneNumber' in result['body']['user'])) {
console.log("User has included a phoneNumber");
} else {
console.log("User has Not included a phoneNumber");
}
},error ...)

Check if json object is undefined in Node.js

I'm getting this error "TypeError: Cannot read property '0' of undefined" when I want to extract a data from JSON file.
However, the data I want to extract is not available every time I request a JSON file, therefore, I'm getting this error which makes my Node.js Application to crash every time I'm getting this error.
simply check if it exists or not:
if (json && json['Name'] && json['Name']['Nationality']) {
data = json['Name']['Nationality'][0];
} else {
// no data, do whatever error handling you want here
}
A solution for this sort of problem is using try-catch:
try {
data = json['Name']['Nationality'][0];
} catch (error) {
data = "Sorry no data found"
}
The try function is going to run the code if it did find any error it will pass it to catch.

JSON - extract call from REST call

I am using a third party database which has a rest API. When I make a call I get an error back (which I am expecting in my case):
transaction.commit(function(err) {
if (err){
var par = JSON.parse(err); \\ returns error: SyntaxError: Unexpected token E in JSON at position 0
console.log(JSON.stringify(err));
console.log(err);
console.log('' + err);
//First console.log return: {"code":409,"metadata":{"_internal_repr":{}}}
//Second console.log return: { Error: entity already exists: app: "s~myapp"<br/>path <<br/> Element {<br/> type: "v"<br/> name: "bob#gmail.com"<br/> }<br/>><br/>
//Third console.log returns: Error: entity already exists: app: "s~myapp"<br/>path <<br/> Element {<br/> type: "v"<br/> name: "bob#gmail.com"<br/> }<br/>><br/>
}
{);
I need to extract the error field and the type field. I have tried to parse the JSON and then go par.error or par.type to get the variables, but I can't parse the object because I get an error.
You're apparently having an Error object, that has a message property to extract the message string.
Hence use
err.message
to obtain it.
References:
Error.prototype.message
Error
Based on your results, it seems that the err parameter that you're getting is already an object and not a JSON string, so you don't need to parse it at all.
You should be able to get err.code without problem.
You did mention that you need to get the error type -- but that seems to not be available in that object at all (and that's why you'd get undefined while you tried it.
However, by using err.Error you should be able to get the error string.
If you're unsure of what data the object has, you can try the following:
Execute console.dir(err) -- this should give you a good understanding of what the err object contains.
Just debug up to that point and look around in the err object.
(Best option) Check the platform's / libraries API documentation, it should tell you what error information it returns so you can use exactly that.

Type refining an `any` type in flowtype

I seem to be misunderstanding how flowtype refinements work (or maybe my Nuclide/Atom + Flow setup is being dumb). I'd like to do something like the following:
async function getIp(): Promise<string> {
const resp = await fetch('https://httpbin.org/ip')
const json = await resp.json() // `json: any` at this point
if (typeof json.ip === 'string') {
return json.ip
} else {
throw new Error("Weird response.")
}
}
I'm fetching some JSON from an API endpoint, and it has type any. I'd like to sanity check that it has the right form (e.g. that it has a string ip field). Nuclide however warns me that every use of json in the above code is "Not covered by flow", including the entire json.ip expression. Why is that? I would have expected the typeof check to refine the type of json.ip to string.
Is there another way to refine untyped values?
Edit: Here's a tryflow example of what I'm seeing.
No, you can't refine any. You already can do anything with it, so what's the point?
If you want Flow to verify your code you should immediately convert your any to mixed:
const json: mixed = await resp.json()

Insert an array of documents into a model

Here's the relevant code:
var Results = mongoose.model('Results', resultsSchema);
var results_array = [];
_.each(matches, function(match) {
var results = new Results({
id: match.match_id,
... // more attributes
});
results_array.push(results);
});
callback(results_array);
});
}
], function(results_array) {
results_array.insert(function(err) {
// error handling
Naturally, I get a No method found for the results_array. However I'm not sure what else to call the method on.
In other functions I'm passing through the equivalent of the results variable here, which is a mongoose object and has the insert method available.
How can I insert an array of documents here?
** Edit **
function(results_array) {
async.eachLimit(results_array, 20, function(result, callback) {
result.save(function(err) {
callback(err);
});
}, function(err) {
if (err) {
if (err.code == 11000) {
return res.status(409);
}
return next(err);
}
res.status(200).end();
});
});
So what's happening:
When I clear the collection, this works fine.
However when I resend this request I never get a response.
This is happening because I have my schema to not allow duplicates that are coming in from the JSON response. So when I resend the request, it gets the same data as the first request, and thus responds with an error. This is what I believe status code 409 deals with.
Is there a typo somewhere in my implementation?
Edit 2
Error code coming out:
{ [MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index:
test.results.$_id_ dup key: { : 1931559 }]
name: 'MongoError',
code: 11000,
err: 'insertDocument :: caused by :: 11000 E11000 duplicate key error index:
test.results.$_id_ dup key: { : 1931559 }' }
So this is as expected.
Mongo is responding with a 11000 error, complaining that this is a duplicate key.
Edit 3
if (err.code == 11000) {
return res.status(409).end();
}
This seems to have fixed the problem. Is this a band-aid fix though?
You seem to be trying to insert various documents at once here. So you actually have a few options.
Firstly, there is no .insert() method in mongoose as this is replaced with other wrappers such as .save() and .create(). The most basic process here is to just call "save" on each document you have just created. Also employing the async library here to implement some flow control so everything just doesn't queue up:
async.eachLimit(results_array,20,function(result,callback) {
result.save(function(err) {
callback(err)
});
},function(err) {
// process when complete or on error
});
Another thing here is that .create() can just take a list of objects as it's arguments and simply inserts each one as the document is created:
Results.create(results_array,function(err) {
});
That would actually be with "raw" objects though as they are essentially all cast as a mongooose document first. You can ask for the documents back as additional arguments in the callback signature, but constructing that is likely overkill.
Either way those shake, the "async" form will process those in parallel and the "create" form will be in sequence, but they are both effectively issuing one "insert" to the database for each document that is created.
For true Bulk functionality you presently need to address the underlying driver methods, and the best place is with the Bulk Operations API:
mongoose.connection.on("open",function(err,conn) {
var bulk = Results.collection.initializeUnorderedBulkOp();
var count = 0;
async.eachSeries(results_array,function(result,callback) {
bulk.insert(result);
count++;
if ( count % 1000 == 0 ) {
bulk.execute(function(err,response) {
// maybe check response
bulk = Results.collection.initializeUnorderedBulkOp();
callback(err);
});
} else {
callback();
}
},function(err) {
// called when done
// Check if there are still writes queued
if ( count % 1000 != 0 )
bulk.execute(function(err,response) {
// maybe check response
});
});
});
Again the array here is raw objects rather than those cast as a mongoose document. There is no validation or other mongoose schema logic implemented here as this is just a basic driver method and does not know about such things.
While the array is processed in series, the above shows that a write operation will only actually be sent to the server once every 1000 entries processed or when the end is reached. So this truly does send everything to the server at once.
Unordered operations means that the err would normally not be set but rather the "response" document would contain any errors that might have occurred. If you want this to fail on the first error then it would be .initializeOrderedBulkOp() instead.
The care to take here is that you must be sure a connection is open before accessing these methods in this way. Mongoose looks after the connection with it's own methods so where a method such as .save() is reached in your code before the actual connection is made to the database it is "queued" in a sense awaiting this event.
So either make sure that some other "mongoose" operation has completed first or otherwise ensure that your application logic works within such a case where the connection is sure to be made. Simulated in this example by placing within the "connection open" event.
It depends on what you really want to do. Each case has it's uses, with of course the last being the fastest possible way to do this as there are limited "write" and "return result" conversations going back and forth with the server.

Categories

Resources