const functions = require('firebase-functions')
const axios = require('axios')
exports.getTown = functions.https.onCall((data, context) => {
axios.get(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${data.lat},${data.lng}&result_type=locality&key=**********`)
.then(town => {
return town
}).catch(err => {
return err
})
})
When I call this in the front end I just get an error in the console:
POST https://europe-west2-heretic-hearts.cloudfunctions.net/getTown 500
Uncaught (in promise) Error: INTERNAL
I've tested to make sure the incoming data is being received properly and it is, so the problem must be in the function itself. But I can't see what could possibly be going wrong here...?
You can't invoke an onCall type function with a simple POST request. Callable functions have a specific protocol that they use on top of HTTP. If you can't reproduce that protocol, the function will fail every time.
If you want to write a simple HTTP function endpoint, then follow the instruction for writing an HTTP trigger instead using onRequest. It works very differently.
Also, I'm noticing that you're not handling promises correctly in your function. Please read the documentation thoroughly to understand what you need to do with promises in order to get your function to execute correctly, no matter what type of function you write.
Related
i am working on a ToDo list and its basically done. but i am getting this error in the console that i haven't come across yet, its preventing me to create the list (to do list)
This is the error im getting:
OPTIONS http://localhost:4000/cpds/add
net::ERR_NAME_NOT_RESOLVED
Uncaught (in promise) Error: Network Error createError.js:17
at createError (createError.js.17)
at XMLHttpRequest.handelError (xhr.js:80)
Can someone please explain what this means and how to resolve this issue.
the list prints in my console but not in my browser, then prints this error afterwards.
ERR_NAME_NOT_RESOLVED - points that system fail to resolve IP address for given hostname (http://localhost:4000/cpds/add in your case). While it is very unlikely that you are realy could not resolve address for localhost itself most probable reason is that you requesting for closed port (:4000).
In general this message say Uncaught which means that somewhere in you code when you request for "http://localhost:4000/cpds/add" form axios (it is assumtion cause you don't gave any details about your code) you have statement like
axios.get(url, { headers })
.then(data => console.log(data))
without
.catch(error => console.error(error))
so full version is
axios.get(url, { headers })
.then(data => console.log(data))
.catch(error => console.error(error))
So when request is fails due to any reason (probably error in url in you case) interpreter don't know how to overcome it (other words you should directly define function which would be called in case of error and pass it to catch method).
To ensure error is in url try to place http://localhost:4000/cpds/add to address bar of you browser, if it is realy unaccessable, browser should show you an error.
This is because one of your calls returned a rejected promise/async function, or in other words: An error that occurred calling your function.
Be careful about this. You can write yourlibrarycall.then(result => ...).catch(error => ...) But this can quickly get a pitfall. The catch clause will be called if the library call failed, but also when the .then clause failed. You'd expect the failure came from the library call, but this was fine, your code might also had a problem and the value that the variable error returns might be totally different (or undefined).
Hence i prefer having:
yourFunction = async () => {
let result;
try {
result = await yourlibrarycall // this is blocking
}
catch (error) {
// error handling only of your library call
}
// here comes your following logic
...
}
Using asnyc, your function is executed asynchronously and can now wait for the result using the keyword await. If the library call failed, it will enter the catch scope and provide you a variable with the error occurred.
This is now all the error handling and only will now only cope with the request, the following logic is then executed afterwards, getting rid of the misleading .then(...).catch(...).
If you still want to use the promise approach instead of async/await be careful to handle all the errors in the catch clause explicitly, otherwise they'll bubble up and will be catched by the catch clause, as stated above.
I'm writing integration tests for a class that has a lot of requests. The requests are done through a HttpClient singleton.
So, to avoid making real requests, I mock all calls to HttpClient. The problem is, I have too many requests.
HttpClient.get is called to fetch a token.
HttpClient.get is called to fetch a resource.
HttpClient.get is called to fetch all customers from this resource.
HttpClient.get is called to verify if a single customer exists in another API.
Conditional: HttpClient.post is called to add this one customer to the API, if it does not exist.
HttpClient.post is called to add the resource to another API.
It's actually a little more complicated than that, because some of these calls are done multiple times (inside a loop), but you get the picture.
I wrote a test case for every scenario. One test case to simulate a failed request to fetch the token, another to simulate a failed request to fetch a resource and so on.
To do this, I wrote a "happy" scenario - where everything goes well -, using mockImplementationOnce. My beforeEach looks a little like this:
tokenResponse = { body: { token: 'some-token'}, status: 200 }
HttpClient.get.mockImplementationOnce(() => tokenResponse)
tokenResource = { body: <some-fixture-with-resources>, status: 200 }
HttpClient.get.mockImplementationOnce(() => tokenResource
(...)
To write the scenarios, I reassigned the returned variable
it('fails to fetch the token', () => {
tokenResponse = { status: 500 }
// code that calls my class
// code that asserts that an error was thrown
}
Anyway, I managed to write simple test cases for all scenarios, but my beforeEach has a giant boilerplate. Besides that, now I want to write more advanced test cases where a request is done multiple times (n of customers > 1). It's getting quite complicated to handle all fixtures and keeping track of individual mocks.
Is this a common issue? Is there an easier way to handle mock implementations? I thought about something like mockImplementationNth but couldn't find anything.
Ps.: Changing the code itself is hard because it is legacy code and the APIs are a little clunky.
I thought about isolating the scenarios into a setupMocks function with a default setting that could be overwritten in the test cases with another function. It would look something like this:
describe('Integration test', () => {
beforeEach(() => {
setupMocks()
})
it('goes well', () => {
expect(myClass.execute()).resolves.toBe(true)
})
it('fails to fetch the token', () => {
overrideMocks('token-failed')
expect(myClass.execute()).rejects.toEqual('some-error')
})
(...)
})
At least the test cases will look simpler.
Is there a way to call fetch in a Jest test? I just want to call the live API to make sure it is still working. If there are 500 errors or the data is not what I expect than the test should report that.
I noticed that using request from the http module doesn't work. Calling fetch, like I normally do in the code that is not for testing, will give an error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout. The API returns in less than a second when I call it in the browser. I use approximately the following to conduct the test but I also have simply returned the fetch function from within the test without using done with a similar lack of success:
import { JestEnvironment } from "#jest/environment";
import 'isomorphic-fetch';
import { request, } from "http";
jest.mock('../MY-API');
describe('tests of score structuring and display', () => {
test('call API - happy path', (done) => {
fetch(API).then(
res => res.json()
).then(res => {
expect(Array.isArray(response)).toBe(true);
console.log(`success: ${success}`);
done();
}).catch(reason => {
console.log(`reason: ${reason}`);
expect(reason).not.toBeTruthy();
done();
});
});
});
Oddly, there is an error message I can see as a console message after the timeout is reached: reason: ReferenceError: XMLHttpRequest is not defined
How can I make an actual, not a mocked, call to a live API in a Jest test? Is that simply prohibited? I don't see why this would fail given the documentation so I suspect there is something that is implicitly imported in React-Native that must be explicitly imported in a Jest test to make the fetch or request function work.
Putting aside any discussion about whether making actual network calls in unit tests is best practice...
There's no reason why you couldn't do it.
Here is a simple working example that pulls data from JSONPlaceholder:
import 'isomorphic-fetch';
test('real fetch call', async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users/1');
const result = await res.json();
expect(result.name).toBe('Leanne Graham'); // Success!
});
With all the work Jest does behind the scenes (defines globals like describe, beforeAll, test, etc., routes code files to transpilers, handles module caching and mocking, etc.) ultimately the actual tests are just JavaScript code and Jest just runs whatever JavaScript code it finds, so there really aren't any limitations on what you can run within your unit tests.
I am attempting to use an async/await pattern in order to handle a scenario that might be considered "callback hell" if implemented otherwise.
Here is an extremely dumbed down version of the code. The real code has about 5 conditional HttpClient calls based on the data from the first call (not my api...) which is the reason why I am using the async/await pattern in the first place.
async blah(): Promise<boolean> {
try {
let resp = await this.http.get("https://httpstat.us/500").toPromise();
console.warn("you should not see this");
// the real code will logically call the api multiple times based on conditonal data from resp
// hence the attempted usage of async/await to avoid "callback hell"
// blah() will eventually return an object.
return true;
}
catch (err) {
console.error("caught inside blah()");
throw err;
}
}
ionViewDidLoad() {
this.blah().then(data => {
console.warn('okokokok');
}).catch(error => {
console.error(error)
});
}
What happens, I can see the call actually 500, but the code continues and the following is printed to the console:
polyfills.js:3 GET https://httpstat.us/500/ 500 (Internal Server Error)
main.js:927 you should not see this
main.js:940 okokokok
As you can see, it isn't catching the 500 (or any other http status I have tested with)
The device I am testing with is a Pixel 2 running P and the console data is coming from a Chrome device inspector session.
Any advice would be greatly appreciated.
** Edit: This is clearly an issue with the combination of ionic and angular... It should work...
** Edit: it turns out to 100% be an Angular issue... Not the framework itself but how an interceptor was implemented. I will leave this here instead of deleting the question in the rare case someone else requires it.
If i uderstood your question correctly, you want to do cascade calls, so you make the http request and based on the response you want to do another http call. If that is the case, then you should consider using switchMap operator:
this.http.get("https://httpstat.us/500").pipe(
switchMap( result => {
if(result.a === 5) {
return this.http.get("some server api url");
}
return return this.http.get("another server api url");
})
)
You handle the errors then in rxjs way.
See cascading calls
Nothing happens to my firestore when I call the function below. I also can't see "inside helloWorld" on my GCP logs.
exports.helloWorld = functions.https.onCall((data, context) => {
console.log("inside helloWorld);
const users = admin.firestore().collection('users');
users.doc(data.userId).get().then( // --------------------Line A
(snapshot) => {
if (snapshot.exists) {
console.log("snapshot exists");
return null;
} else {
console.log("inside else");
users.doc(data.userId).set({
name: data.name
});
return null;
}
}
).catch(() => 'obligatory catch');
return; //-----------------------------------------------Line B
});
However, when I place the return on Line A, the function works as expected and a document is created in my firestore. "inside helloWorld" is shown on my GCP logs.
Why is that so?
I really appreciate any levels of clarification.
According to the documentation for callable functions (particularly the part about sending back a result):
To return data after an asynchronous operation, return a promise. The
data returned by the promise is sent back to the client. For example,
you could return sanitized text that the callable function wrote to
the Realtime Database.
Even if you don't want to send any content in the response, Callable functions still need to respond to the HTTP request that originated them, as all HTTP transactions do.
In either case, you still need to make use of the promises in all your async calls so that Cloud Functions knows when to respond to the client, after all the work is complete. Placing the return statement on "line A" is effectively returning the promise from the async work started by get(), fulfilling this requirement. Without the return statement, Cloud Functions is terminated your function because it thinks there is no more work to complete in order to send the final response.
If you're not familiar about how promises work in JavaScript, watch my video tutorials here: https://firebase.google.com/docs/functions/video-series/