In my Angular application service I have a method that makes a call to a mock JSON:
my.service.ts:
...
private baseUrl: string = 'http://localhost:9999/accounts-grid.json';
...
loadAccounts() {
if (this.dataStore.responseObject) {
this.refreshDataStore();
}
let token = this.authenticationService.getToken();
let headers = new Headers({ 'netx.esf.AuthenticationService': token });
let options = new RequestOptions({ headers: headers });
this.http.get(`${this.baseUrl}/positions/priorday/${accountNumber}`, options)
this.http.get(`${this.baseUrl}`, options)
.map((response: Response) => response.json())
.subscribe(
...
error => {
this.logService.error('loadAccountList() exception:', error);
this.setError(this.message[0], error._body); });
return this.responseObject$;
}
I would like to be able to load a different dummy JSON with the same call depending how many times the method was called. For example, the first time I call loadAccounts(), I would like to get a response from accounts-grid.json, the second time I make this call I would like to get a response from, say, accounts2-grid.json.
Is this possible?
Add a local variable to the service to keep track:
...
private baseUrl: string = 'http://localhost:9999/accounts-grid.json';
private callCount = 0;
...
loadAccounts() {
if ( this.callCount > 0 ) { const newUrl = this.baseUrl.substring(0, this.baseUrl.lastIndexOf('.json')) + this.callCount.toString() + this.baseUrl.substring(this.baseUrl.lastIndexOf('.json')); }
this.callCount += 1;
if (this.dataStore.responseObject) {
this.refreshDataStore();
}
let token = this.authenticationService.getToken();
let headers = new Headers({ 'netx.esf.AuthenticationService': token });
let options = new RequestOptions({ headers: headers });
this.http.get(`${newUrl}/positions/priorday/${accountNumber}`, options)
this.http.get(`${newUrl}`, options)
.map((response: Response) => response.json())
.subscribe(
...
error => {
this.logService.error('loadAccountList() exception:', error);
this.setError(this.message[0], error._body); });
return this.responseObject$;
}
You will probably also want to take care of the callCount item to subtract a count if there's an error, but this is the general idea.
Related
Here is the function that I wanna test, it takes a token and a description as props. Normally in React code, I can get token from useContext.
export const updateUserProfileAbout = async (
token,
description
) => {
const dataUpdateTemplateDescriptionRes = await patchData(`me/`, token, {
about:description,
});
const dataUpdateTemplateDescriptionJson = await dataUpdateTemplateDescriptionRes.json();
return dataUpdateTemplateDescriptionJson;
};
And here is my custom patchData function:
const patchData = async (urn, token, data = "") => {
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${token.access}`,
};
const body = data ? JSON.stringify(data) : null;
let response;
if (body) {
response = await fetch(`${host}/api/${urn}`, {
method: "PATCH",
headers,
body,
});
} else {
response = await fetch(`${host}/api/${urn}`, {
method: "PATCH",
headers,
});
}
if (!response.ok) throw new Error(response.status);
return response;
};
You are right. You don't need the token. All you need to do for mocking the fetch is the following:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.resolve()) as jest.Mock);
If you want to retrieve a specific object from a json response, you can use:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ myObject }) })) as jest.Mock);
You can also reject it to trigger the error catch:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.reject()) as jest.Mock);
If you want to return something multiple times, change the mockImplementationOnce to whatever you need (maybe mockImplementation, for returning it every time you call it).
If you also want to expect the call of the fetch just add a constant:
const myFetch = jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.reject()) as jest.Mock);
You can then expect it via: expect(myFetch).toBecalledTimes(1);
After one more day of researching, I might be wrong though but I don't think I have to care about token or authorization when unit testing for front-end. All I need is jest.fn() to mock function and jest.spyOn(global, "fetch") to track fetch API.
For more information, here are some references that I read:
https://codewithhugo.com/jest-fn-spyon-stub-mock/
https://dev.to/qmenoret/mocks-and-spies-with-jest-32gf
https://www.pluralsight.com/guides/how-does-jest.fn()-work
https://www.loupetestware.com/post/mocking-api-calls-with-jest
So I moved over a non-reusable fetch request code snippet to my API:
let response = await fetch(visitURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + userJWT
},
body: JSON.stringify(endingVisit)
});
if (response.ok) {
let {visitId, createdAt} = await response.json();
const viewVisitDto = new ViewVisitDto(`${visitId}${createdAt}${visitorId}${doctorId}${oldPatientId}`);
return viewVisitDto;
} else {
throw new Error("deactivated!")
}
I was able to get this far:
axios.post(visitURL, {
headers,
body: JSON.stringify(visit)
}).then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
})
But does not exactly give me the visitId and createdAt from the response and I cannot use a response.ok nor a response.json(). Essentially I need to pull out that visitId and createdAt that should be coming back in the response.
I also tried just using node-fetch library, but although in VS code it seems to accept it, TypeScript is not happy with it even when I do install #types/node-fetch and even when I create a type definition file for it, my API just doesn't like it.
Guessing what you are after is
// don't know axios, but if it returns a promise await it
const dto = await axios.post(visitURL, {
headers,
body: JSON.stringify(visit)
}).then((response) => {
// parse response
return {resonse.visitId, resonse.createdAt}
}).then(({visitId, createdAt}) => {
// form dto (where are other vals)?
return new ViewVisitDto(`${visitId}${createdAt}${visitorId}${doctorId}${oldPatientId}`);
}).catch((error) => {
console.log(error);
})
However - you don't mention where doctorId and oldPatientId come from... You try providing more info, including output of the console.log's and the surrounding code
I am trying to make an API call from my JavaScript app to an endpoint in another application.
When I call the endpoint I get the status code and the message, but I cannot access the response body in any way. I have tried different ways to get the data, but nothing seems to work for me.
In the method "someAction", I want to use the data in the response/result from the API call. I added the outputs that the single log-lines print in the code.
How can "result" be undefined while "result.status/message" are defined?
What do I have to do in order to get the data/body as JSON or String?
The API itself is tested and returns data when tested in postman.
Thanks for your help!
const request = require('request')
let callEndpoint = () => {
return new Promise((resolve, reject) => {
const url = `https://my.api.com/endpoint`
const requestOptions = {
url: url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
'My-API-Authorization': '123456789'
},
json: true,
strictSSL: false
}
request.get(requestOptions, (err, response) => {
if(err)
{
return reject(err);
}
return resolve(response);
});
});
}
let someAction = () => {
callEndpoint()
.then(result => {
logger.logInfo(result.statusCode) // => 200
logger.logInfo(result.statusMessage) // => OK
logger.logInfo(result) // => undefined
logger.logInfo(result.toString()) // => [object Object]
logger.logInfo(result.body) // => undefined
logger.logInfo(result.data) // => undefined
JSON.parse(result.toString()) // => Unexpected token o in JSON at position 1
JSON.parse(result) // => Unexpected token o in JSON at position 1
// DO SOME STUFF WITH THE RESULT
})
}
So I am trying to do a POST request in Angular from an array. Basically, when a user selects multiple items in a list they can "unlock" each item. So the issue I am running into is how to do a POST with a forEach. I was able to do a POST with a forLoop but the issue is that when it does one POST it does not do the other one. Can someone please point out what I am doing wrong or if there is a better solution to this problem?
Here are the other Stack questions I looked through to find a possible solution:
Http request in forEach function. Angular2
Angular http post on loops
Chaining http calls in angular 2 in a for loop
component.ts
locked: Array<any> = [];
// Unlock
unlock() {
let observer = {
next(data) {
data => console.log(data)
},
error(error) {
return new Error('Post Error');
},
complete() {
console.log("Completed");
// window.location.reload();
}
}
// On unlock send lock data to Service for POST
this.http.postUnlock(this.unlocked).subscribe(observer);
}
service.ts
// POST to UNLOCK RESOURCES
postUnlock(locked) {
let headers = new Headers();
headers.append( 'Content-Type', 'application/json');
headers.append('Access-Control-Allow-Origin', '*');
locked.forEach((lock) => {
let newBody = JSON.stringify(lock);
let auth = lock.AuthID;
let form = lock.FormName;
let options = new RequestOptions({ headers: headers, method: 'post', body: newBody });
let newUrl = this.url + `?authid=${auth}&formname=${form}`;
// POST to URL
return this.http.post(newUrl, options).map(res => res.json());
});
}
Is it something that has to do with the Observable or is this something that can be handled with Promises?
Thank you for your help.
here is a mix between what you looked for (Http request in forEach function. Angular2) and your code.
you cannot return a value from a for each it will exits the function (What does `return` keyword mean inside `forEach` function?)
hope it helps
postUnlock(locked){
//create an array of Observables
let allQueries = locked.map(lock => {
let newBody = JSON.stringify(lock);
let auth = lock.AuthID;
let form = lock.FormName;
let options = new RequestOptions({ headers: headers, method: 'post', body: newBody });
let newUrl = this.url + `?authid=${auth}&formname=${form}`;
// POST to URL
return this.http.post(newUrl, options).map(res => res.json());
});
//return a new observable that will emit all the http results
return Observable.forkJoin(allQueries)
}
I'm sending a request to an API, it returns an array of data, but I don't know how to extract the headers from that url, this is what i've tried in my service
#Injectable()
export class ResourcesService {
private resourcesurl = "http://localhost:9111/v1/resources";
constructor(private http: Http) { }
getResources() {
let headers = new Headers();
headers.append("api_key", "123456");
return this.http.get(this.resourcesurl, { headers: headers
}).map(this.extractData).catch(this.handleError);
}
getresourceheaders(){
let headers = new Headers();
headers.append("api_key", "123456");
let options = new RequestOptions();
let testsss = options.headers
let headerapi = this.http.request(this.resourcesurl, options);
let test = this.http.get(this.resourcesurl, { headers: headers });
console.log(headerapi);
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
private handleError(error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
I want to get the headers from that response that in this case is resourceurl
any idea?
Clear angular 5 answer
By default, this.http.whatever's returned observable will be on the data returned, not the HttpResponse.
If you have a peak at: https://angular.io/api/common/http/HttpClient
You'll notice the options take an "observe" parameter of a HttpObserve type. While it's not documented what the HttpObserve is, if you put it as "response" then you will instead receive an instance of HttpResponse<T>(https://angular.io/api/common/http/HttpResponse)
So, here's an example request:
this.http.get(url, {observe: 'response'})
.subscribe(resp => console.log(resp.headers))
Note: Due to browser cors security, you will not be-able to see headers unless the API provides Access-Control-Expose-Headers: with your custom headers if your api and angular app do not have the same domain.
The headers are part of the Response class, so you should be able to see them in a handler like
http.get('/path/to/resource')
.subscribe((res:Response) => {
console.log(res.headers);
// you can assign the value to any variable here
});
When you do .map(this.extractData) the let body = res.json() from this.extractData function takes out everything from the response except the body.
Instead if you do following, .map((res: Response) => res), that will return the whole response and you can access all the attributes and assign them to variables.
Here's a Plunker demo.
A bit more of an exotic example in Angular 5 shown below. Using HttpClient to post to a GraphQL server, read the response and then extract a response header value and a response body value. The header is Total-Count in this case. cars is a field (array of Car) under another field data in the body. Also shows use of the rxjs first operator.
import { HttpClient, HttpHeaders, HttpResponse } from '#angular/common/http';
import { first } from 'rxjs/operators/first';
import { Car, CarPage } from '../models/car';
..........
..........
public find(filter: string, sort: string, limit: number): Observable<CarPage> {
let headers = new HttpHeaders().set("Content-Type", "application/graphql");
let carPage: CarPage = { cars: [], totalCount: 0 };
return this.http.post<HttpResponse<any>>('/graphql',
`query cars { cars(filter: "${filter}", sort: "${sort}", limit: ${limit}) {
id
make
model
year
}
}`,
{ headers: headers, observe: "response" }
)
.first((_, index) => index === 0, (response: HttpResponse<any>) => {
let totalCountHeaderValues = response.headers.getAll("Total-Count");
carPage.totalCount = (totalCountHeaderValues.length > 0) ? parseInt(totalCountHeaderValues[0]) : 0;
carPage.cars = response.body.data.cars;
return carPage;
})
}
The return type of the angular Http.get method returns a Response type. This object has a headers object that contains information about the headers. It also has a url property.
this.http.get(url).map(resp => console.log(resp));