What's the right way to catch errors using the Google Classroom API?
I'm trying to do something like:
gapi.client.classroom.courses.list({
pageSize: 100
}).then((response) => {
return process(response);
});
But if a Google user who doesn't have a Classroom account executes this code, it throws an error, and I'm unclear on how to catch/handle it.
I've tried wrapping this block of code with a try {...} catch(error) {...} and that doesn't catch it. I've also tried adding a .catch(function(error){ ... }); after then .then() statement and that also doesn't seem to work.
The answer is here:
https://developers.google.com/api-client-library/javascript/features/promises
The .then call takes two arguments. The first is a function invoked on success, and the second is a function invoked on failure:
.then(
function(response) {
return process(response);
},
function(errorResponse) {
return handleError(errorResponse);
}
);
Related
Slack API noob here. Trying this out, I gave scope access to user and using user token instead of bot, this is only for testing purposes for now. But I get 'object Promise' on the console. I've tried a few different approaches using .then but haven't been able to figure it out. What am I doing wrong? Thanks for your help, please let me know if you need any additional details.
const result = app.client.search.messages({
token: user_token,
query: "Hello world in:testing"
});
console.log ("result: " + result);
You might want to read up on how Javascript promises work.
Simply put though, result here is a Promise, which is why you are getting that returned in your console.log. A promise is basically an incomplete request that is designed to happen asynchronously. So whilst your code is off making a http request it doesn't wait around, and just runs onward.
You can resolve this by either using async/await (which may also deserve some research), that looks like this:
async function () => {
const result = await app.client.search.messages({
token: user_token,
query: "Hello world in:testing"
});
console.log(result)
}
Or you can use promise chaining (then), like this:
const result = app.client.search.messages({
token: user_token,
query: "Hello world in:testing"
})
.then(result => console.log(result))
.catch(err => console.err(err))
You can chain multiple thens together, and the catch is in case the request errors.
It seems like app.client.search.message returns a javascript promise instead of your search result. Basically, the result of your search isn't ready immediately (it's gotta talk on the interwebs to answer your search), so the slack API is returning a promise instead.
Promises are a whole can of worms. This seems like a fairly decent quick start guide:
https://www.digitalocean.com/community/tutorials/understanding-javascript-promises
Approach 1: async await
The quickest way to get what you want would be to use the await keyword.
This basically says "please wait until this is done and then give me the result"
so this might get you one step closer to what you want:
async function myFunction() {
// do some other stuff
const result = await app.client.search.messages({
token: user_token,
query: "Hello world in:testing"
});
console.log(result)
// do some other stuff
}
Why did I wrap your code in an async function you ask? It's because the await keyword can only be used in async functions. This has a problem you'll have to deal with in that if you simply call an async function like myFunction() it will return before it is done executing. This can cause a race condition (actually, just a sequence error in JS if you want to be pedantic, but it's rarely useful to make that distinction). You could also await myFunction(), but then you're in the same problem as before.
Race conditions / dealing with async functions are too big of a subject to tackle here.
SO. that leads me to
Approach 2: the .then()
It sounds like you already started trying to use .then. I'll try to give an example of how you might want to use it.
// I'm storing the promise in a variable instead of calling .then immediately
// it might make this example easier to understand
const resultPromise = app.client.search.messages({
token: user_token,
query: "Hello world in:testing"
});
function doSomethingWithTheResult (result) {
console.log ("result: " + result);
}
resultPromise.then(doSomethingWithTheResult);
// notice we ARE NOT calling doSomethingWithTheResult
// we are simply telling the promise what function to call when it's done.
The example above uses a bit more variables than people usually use. The code below is basically the same, but a little more compact
app.client.search.messages({
token: user_token,
query: "Hello world in:testing"
})
.then(function (result) { // you could also use an arrow function here
console.log(result)
})
Important Note!
const resultPromise = app.client.search.messages({
token: user_token,
query: "Hello world in:testing"
})
.then(function (result) {
// CODE HERE GETS EXECUTED SECOND (in most cases)
})
// CODE HERE GETS EXECUTED BEFORE CODE IN THE THEN (in most cases)
Important Note 2: Error handling
async await
try {
const result = await funcThatReturnsAPromise()
}
catch (e) {
// something bad happened
// maybe log the error, or display an error message.
}
.then()
funcThatReturnsAPromise()
.then(function(result) {
// if here, then we made it
console.log(result)
})
.catch(function (error) {
// if here, you won't be getting your result today :(
// maybe log the error or something
})
I am encountering an issue I do not understand.
Inside one service I have the following code.
when the code hits PATCH LINE, it jumps immediately to RETURN NOTHING LINE.
the catchError line is not hit.
the () line is not hit
the err line is not hit.
I have compared this to working services and I do not see any difference.
patchItem(item_: Project): Observable<Project> {
const url: string = `${this.serviceUrl}/${item_.id}`;
const data = JSON.stringify(item_);
console.log('inside patch item');
console.log(url);
this.http.patch(url, data, httpOptions) //PATCH LINE
.pipe(
catchError(err => this.handleError('PatchItem', err))
)
.subscribe((response) => {
console.log('item patched ');
return this.myResponse.push(response);
}
, err => console.log("inline error encountered")
,() => console.log("this seems to have completed")
);
console.log("return nothing"); //RETURN NOTHING LINE
return null;
}
The API is C# webapi 2
It is being hit.
There is a problem though, I am expecting the JSON to be passed in and webForm_ is always NULL.
This is probably an important clue.
Again, i compare this to working calls and can not find a difference.
When I inspect the variable in jquery, it has the correct value.
In postman, i get the expected response.
[System.Web.Http.HttpPatch]
[Route("{itemId_}")]
public IHttpActionResult PatchById([FromUri] int itemId_, [FromBody] Models.mpgProject webForm_)
{
return JSONStringResultExtension.JSONString(this, "foobar for now", HttpStatusCode.OK);
}
To save a cycle, here is the handleError function.
I have copy/pasted this from a service that is working as expected.
protected handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error); // log to console instead
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
How can Angular be skipping all the subscription stuff when it IS actually calling the API?
I think it has to be a simple syntax issue, but I have been staring at it for hours... :-(
Happy to post any other code requested...
tyia
ps - I would also be happy to retitle the question. I am at a loss for how to phrase a good question title for this...
Your handleError method returns a function (that returns an Observable) when it should return an Observable.
It looks as if the error handler you copied from another service does not fit here.
I could imagine an error handler like this:
private handleError<T>(operation = "operation", error: HttpErrorResponse) {
console.error(error); // log to console instead
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of({} as T);
}
This method returns an Observable of an empty object. However, this might not be the best option to react on a failing HTTP PATCH request. You would better throw that error up to the component and let the user retry.
I have the following interceptor on my axios reponse :
window.axios.interceptors.response.use(
response => {
return response;
},
error => {
let errorResponse = error.response;
if (errorResponse.status === 401 && errorResponse.config && !errorResponse.config.__isRetryRequest) {
return this._getAuthToken()
.then(response => {
this.setToken(response.data.access_token, response.data.refresh_token);
errorResponse.config.__isRetryRequest = true;
errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
return window.axios(errorResponse.config);
}).catch(error => {
return Promise.reject(error);
});
}
return Promise.reject(error);
}
);
The _getAuthToken method is :
_getAuthToken() {
if (!this.authTokenRequest) {
this.authTokenRequest = window.axios.post('/api/refresh_token', {
'refresh_token': localStorage.getItem('refresh_token')
});
this.authTokenRequest.then(response => {
this.authTokenRequest = null;
}).catch(error => {
this.authTokenRequest = null;
});
}
return this.authTokenRequest;
}
The code is heavily inspired by https://github.com/axios/axios/issues/266#issuecomment-335420598.
Summary : when the user makes a call to the API and if his access_token has expired (a 401 code is returned by the API) the app calls the /api/refresh_token endpoint to get a new access_token. If the refresh_token is still valid when making this call, everything works fine : I get a new access_token and a new refresh_token and the initial API call requested by the user is made again and returned correctly.
The problem occurs when the refresh_token has also expired.
In that case, the call to /api/refresh_token returns a 401 and nothing happens. I tried several things but I'm unable to detect that in order to redirect the user to the login page of the app.
I found that in that case the if (!this.authTokenRequest) statement inside the _getAuthToken method returns a pending Promise that is never resolved. I don't understand why this is a Promise. In my opinion it should be null...
I'm a newbie with Promises so I may be missing something !
Thanks for any help !
EDIT :
I may have found a way much simpler to handle this : use axios.interceptors.response.eject() to disable the interceptor when I call the /api/refresh_token endpoint, and re-enable it after.
The code :
createAxiosResponseInterceptor() {
this.axiosResponseInterceptor = window.axios.interceptors.response.use(
response => {
return response;
},
error => {
let errorResponse = error.response;
if (errorResponse.status === 401) {
window.axios.interceptors.response.eject(this.axiosResponseInterceptor);
return window.axios.post('/api/refresh_token', {
'refresh_token': this._getToken('refresh_token')
}).then(response => {
this.setToken(response.data.access_token, response.data.refresh_token);
errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
this.createAxiosResponseInterceptor();
return window.axios(errorResponse.config);
}).catch(error => {
this.destroyToken();
this.createAxiosResponseInterceptor();
this.router.push('/login');
return Promise.reject(error);
});
}
return Promise.reject(error);
}
);
},
Does it looks good or bad ? Any advice or comment appreciated.
Your last solution looks not bad. I would come up with the similar implementation as you if I were in the same situation.
I found that in that case the if (!this.authTokenRequest) statement inside the _getAuthToken method returns a pending Promise that is never resolved. I don't understand why this is a Promise. In my opinion it should be null...
That's because this.authTokenRequest in the code was just assigned the Promise created from window.axios.post. Promise is an object handling kind of lazy evaluation, so the process you implement in then is not executed until the Promise was resolved.
JavaScript provides us with Promise object as kind of asynchronous event handlers which enables us to implement process as then chain which is going to be executed in respond with the result of asynchronous result. HTTP requests are always inpredictable, because HTTP request sometimes consumes much more time we expect, and also sometimes not. Promise is always used when we use HTTP request in order to handle the asynchronous response of it with event handlers.
In ES2015 syntax, you can implement functions with async/await syntax to hanle Promise objects as it looks synchronous.
I'm trying to call an async lambda function from within another lambda function, and I'm finding that it doesn't get executed if the calling function exits too quickly.
In other words, the following doesn't ever work. LambdaFunction2 never gets called.
function lambdaFunction1(event, context) {
callLambdaFunction2();
context.done(null);
}
But adding a small delay before LambdaFunction1 exits does tend to work so far:
function lambdaFunction1(event, context) {
callLambdaFunction2();
setTimeout(
function() {
context.done(null);
}, 500
);
}
What concerns me is that waiting 500ms is a rather arbitrary magic number. Has anyone encountered a similar problem and found a more principled fix?
callLambdaFunction2() probably does not complete before context.done(null) causes handler to exit.
To fix this you would need to call context.done as a callback. For example:
lambda.invoke({
FunctionName: "functionName",
InvocationType: 'Event',
Payload: JSON.stringify(event)
}, function(err, data) {
if (err) return context.done(err, null);
return context.done(null, data);
});
If this is not the solution, can you show how you have implemented callLambdaFunction2?
I need to invoke some common methods before an AJAX call is made and after the AJAX call (before the actual handler method is called) is success. I'm using dojo.aspect to achieve this.
This is my code sample
function makeAjaxCall(){
dojo.xhrGet({
url:"sample_url",
content:{
test:"value"
},
load:function(response){
//Do some logic here
},
error:function(response){
//handle error
}
});
}
Below is the dojo.aspect which I'm using to get a hook to the XHR calls.
define(["dojo/aspect"], function(aspect){
aspect.after(dojo, "xhr", function(deferred){
console.log("AJAX AFTER");
deferred.then(function(response){
//CALLED AFTER 'load' METHOD IS CALLED.
console.log("Testing");
});
});
aspect.before(dojo, "xhr", function(method, args){
console.log("AJAX BEFORE");
});
});
Now the problem is deferred.then inside aspect.after is called after the "load" function is called. Is it possible to have a method which is called before the actual load method is invoked?
The short answer is yes.
First, there are two ways to make ajax calls in Dojo.
dojo/xhr - this is what you have above and this is deprecated
in favor of
dojo/request/xhr
The first implementation will call into the second implementation. So I would recommend using aop on dojo/request/xhr.
aspect.around(require.modules['dojo/request/xhr'], 'result', function(originalXhr){
return function(url, options, returnDeferred){
var dfd = new Deferred();
// Logic before making the xhr call
originalXhr(url, options, returnDeferred)
.then(function(response) {
// Logic handling the response but before resolving the deferred.
dfd.resolve(vm);
// Logic after resolving the deferred.
}, function(err){
// error handling?
dfd.reject(msgs);
}, function(update) {
dfd.progress(update);
});
return dfd;
};
});
You can find the complete implementation at
https://github.com/cswing/evinceframework/blob/master/evf-web-js/src/dojo/evf/serviceRegistry.js (~ line 111)
USAGE:
require('dojo/xhr/request', function(xhr){
xhr({...}).then(
function(response) {
//handle response
},
function(error) {
//handle error
}
);
});
The dojo/xhr code will translate itself to the usage above, so the code you posted should work.
If you switch to the new API - dojo/request
Then you could use dojo/request/xhr and dojo/request/notify
In Dojo 1.10 there is new API to globally catch state of requests.
notify("error", function(error){
console.error(error);
//SyntaxError: Unexpected token < in JSON at position 0(…)
});
But in my case I get errors in html eg. so in error I get "error SyntaxError: Unexpected token < in JSON at position 0(…)"
In previous version there was an access to response object:
topic.subscribe("/dojo/io/error", function(/*dojo.Deferred*/ dfd, /*Object*/ response){
if (response.status === 401) {
window.location.reload();
}
});
So I figured out that json handler can be customized:
require(["dojo/request/handlers"], function(handlers){
handlers.register("json", function(response){
if (response.status === 401) {
window.location.reload();
return;
}
return JSON.parse(response.text || null);
});
});
This way you are able to detect response.errors before JSON.parse throws exception.