Read header from response with apollo-datasource-rest - javascript

import { RESTDataSource } from 'apollo-datasource-rest';
export class Foo extends RESTDataSource {
async getFoo(id) {
const responseFoo = await this.get(`/api/v1/foo/${id}`);
return FooReducer(responseFoo);
}
}
Lets say I use this.get go do a GET Request. How can I read the headers in the response? Is this completely missing in the apollo-datasource-rest package?

In the didRecieveResponse method in the RESTDataSource, you can access the response object. Define/override as follows in the RESTDataSource class:
export class Foo extends RESTDataSource {
async didReceiveResponse(response, request) {
// use this.authHeader value in the class anywhere
this.authHeader = response.headers.get('authorization');
}
async getFoo(id) {
const responseFoo = await this.get(`/api/v1/foo/${id}`);
console.log(this.authHeader)
return FooReducer(responseFoo);
}
}
The the willSendRequest method (where you can access the req object and set headers etc) runs before firing an http request and didReceiveResponse method is called after receiving a response from an http request.

Related

Typescript Error with Axios API response - array methods do not exist on response

I am having an issue with an axios response and a typescript error -
The Error that i am getting is Property 'filter' does not exist on type 'ISearchServiceResponse'.
How do I tell typescript that the object that i want to work with is an array and not the axios response after it has come back from the server?
useEffect(() => {
if (apiKey) {
const getUserFilters = async () => {
try {
const res = await searchService.filters.get(apiKey)
const responsesWithoutKeywordAsset = res.data
.filter(item => !item.key.includes('BAR'))
.filter(other => !other.key.includes('FOO'))
} catch (err) {
console.error(err)
}
}
getUserFilters()
}
}, [apiKey])
type Res<T> = Promise<AxiosResponse<T>>
export const searchService = {
filters: {
get: (apiKey: string): Res<SearchService.ISearchServiceResponse> =>
axiosInstance.get(
www.myapicall.com?aggregation=PAYLOAD_CATEGORY`,
{
headers: {
'X-Api-Key': apiKey
}
})
}
}
export namespace SearchService {
export interface ISearchServiceResponse {
data: Filters[]
}
export interface Filters {
key:string
value: string
}
}
General advice
I've personally never defined my interfaces directly using AxiosResponse.
Instead, I've made use of the generic methods in the Axios typings.
For example:
const response = axios.get<User[]>("/users");
In this case, response will have a type of AxiosResponse<User[]>.
Meaning that response.data has a type of User[], which will have a filter-function.
Your specific issue
If I read your code well, then Res<SearchService.ISearchServiceResponse> is equal to Promise<AxiosResponse<SearchService.ISearchServiceResponse>>.
If you unwrap the promise, then you're left with AxiosResponse<SearchService.ISearchServiceResponse>.
This means that AxiosResponse has a field data of type SearchService.ISearchServiceResponse.
This data, also has a data field as defined by your interface.
So a call to response.data.data.filter would work.
But I'd change the interface into something that makes more sense.
Suggestion from comments
It was suggested in the comments (by #marita-farruggia) to let ISearchServiceResponse extend from AxiosResponse which would work.
However, I'd define it as follows:
interface ISearchServiceResponse extends AxiosResponse<Filters[]> {}
Note: you'd then need to change your definition of Res<T> to Promise<T> (which makes the Res type an unnecessary abstraction of Promise in my opinion).

How to use cookie inside `getServerSideProps` method in Next.js?

I have to send current language on endpoint. But getting language from Cookie returns undefined inside getServerSideProps.
export async function getServerSideProps(context) {
const lang = await Cookie.get('next-i18next')
const res = await fetch(`endpoint/${lang}`)
const data = await res.json()
return {
props: { data },
}
}
export default Index;
What is the proper way to get cookie inside getServerSideProps?
You can get the cookies from the req.headers inside getServerSideProps:
export async function getServerSideProps(context) {
const cookies = context.req.headers.cookie;
return {
props: {},
};
}
You could then use the cookie npm package to parse them:
import * as cookie from 'cookie'
export async function getServerSideProps(context) {
const parsedCookies = cookie.parse(context.req.headers.cookie);
return { props: {} }
}
To avoid having to parse the cookies string from context.req.headers.cookie, Next.js also provides the cookies as an object which can be accessed with context.req.cookies.
export async function getServerSideProps(context) {
const lang = context.req.cookies['next-i18next']
// ...
}
From getServerSideProps documentation:
The req in the context passed to getServerSideProps provides built in
middleware that parses the incoming request (req). That middleware is:
req.cookies - An object containing the cookies sent by the request.
Defaults to {}
You can use parseCookies function with cookie package
import cookie from "cookie"
function parseCookies(req){
return cookie.parse(req ? req.headers.cookie || "" : document.cookie);
}
And then get access like that.
export async function getServerSideProps({ req} ) {
const cookies = parseCookies(req);
// And then get element from cookie by name
return {
props: {
jwt: cookies.jwt,
}
}
}
If you are using Axios this is very simple
This will work inside getServerSideProps method. You can't get access to the cookie by using withCredentials because this is on the server.
const { token } = context.req.cookies;
const response = await axios.get('/staff/single', {
headers: { Cookie: `token=${token};` },
});
or try (This will work on the client)
const response = await axios.get('/staff/single', {
headers: { withCredentials: true },
});
how are you doing?
you can use Something like this :
export async function getServerSideProps(context) {
console.log(context.req.cookies)
}
so easy and so beautifuly!

How can I test vuex action which with jest which has an api call inside it?

ASYNC ACTION VUEX : This is async action which will be called from the
component, it consists of an function fetchGaragesData that will make
an api call to fetch data from server.
[ACTION_TYPES.FETCH_CASHLESS_GARAGES]: async (
{ dispatch, commit, state, rootState },
payload
) => {
commit(MUTATION_TYPES.SET_MISC_KEYS, {
fetchingCashlessGaragesInitiated: true,
})
let { insurerSlug, makeId, rtoCode } = payload
const url = ApiUrls.getCashlessGarages + `${insurerSlug}/${makeId}`
const response = await fetchGaragesData(url, rtoCode)
dispatch(ACTION_TYPES.MODIFY_RTO_WISE_GARAGES_DATA, response)
},
IMPLEMENTATION OF fetchGaragesData: this function internally calls
axios get:
export const fetchGaragesData = (url: string, rtoCode: string) => {
return get(url, {
rto_code: rtoCode,
all_states_data: true,
})
}
How can I test action ACTION_TYPES.FETCH_CASHLESS_GARAGES ???
You might be having API mock response. So, in your spec file you need to add the mock response for that particular API call.
Include your file in spec file which has your actual backend API call.
For example:
import someName from 'path of file'
jest.mock('someName', () => ({
fetchGaragesData: jest.fn((url, rtoCode) =>
success({ body:
Here comes your response body
}})
)
url and rtoCode should be the name of the variables used in your API call file.

How to test a public async function inside Class using Jest in a ReactJS/Typescript app

Property 'getUsersTotalPayout` does not exist on type typeof PayoutApi
My Class:
import { bind } from 'decko';
import BaseApi from './Base';
import * as NS from './types';
class PayoutApi extends BaseApi {
#bind
public async getUsersTotalPayout(userId: string): Promise<number> {
const params: NS.IGetUsersTotalPayoutRequest = { userId };
const response = await this.actions.get<{ payout: number }>(
'/api/get-total-payout',
params,
);
return response.data.payout;
}
}
export default PayoutApi;
The test file:
import PayoutApi from './LiquidityPool';
const endpoint = '/api/get-total-payout';
const userId = 'foo';
jest.mock(endpoint, () => ({
getUsersTotalPayout: jest.fn(() => Promise.resolve({ data: { payout: 100.21 } }))
}));
describe('API: getUsersTotalPayout', () => {
it('should make a request when we get images', () => {
// const testApi = new PayoutApi();
expect(PayoutApi.getUsersTotalPayout(userId)).toHaveBeenCalledWith(endpoint, 'GET');
});
});
Getting that error on expect(PayoutApi.getUsersTotalPayout).toHaveBeenCalledWith(endpoint, 'GET');
You are currently trying to call method on the class. Since it is not static you should instantiate object of class first.
let api = new PayoutApi();
expect(api.getUsersTotalPayout(userId).....)
since jest.mock mocks module not endpoint or XHR request your test will try sending live request to /api/get-total-payout. For handling that it's needed to know what XHR wrapper do you use. Say for fetch() there is nice wrapper-mocker and libraries like axios also have their equivalence.
As for test itself. It does not work if you call a method and make expect on its result. It should be running method that must call server and then checking for mocked XHR if it has been called with valid parameters:
api.getUsersTotalPayout(userId);
expect(fetch_or_other_wrapper_for_mocking_xhr.get_last_request_method()).toEqual('get', endpoint, userId)

javascript - call exported function from another class

Here's the scenario: There is a file called api.js which has method api() to make api calls. There is another class called AutoLogout which has functionality to show autologout modal and logging out user after certain time in case of no activity. These works fine.
index.js in ../services
export { default as api } from './api';
// export { api, onResponse } from './api'; tried this as well
export { default as userService } from './userService';
api.js
import userService from './userService';
export function onResponse(response) {
// returns response to calling function
return response;
}
async function api(options) {
const settings = Object.assign(
{
headers: {
'content-type': 'application/json',
'x-correlation-id': Math.random()
.toString(36)
.substr(2),
},
mode: 'cors',
credentials: 'include',
body: options.json != null ? JSON.stringify(options.json) : undefined,
},
options,
);
const response = await window.fetch(`/api/v0${options.endpoint}`, settings);
// calling onResponse() to send the response
onResponse(response);
if (response.status === 403) return userService.logout();
if (response.status > 299) throw new Error();
if (response.status === 204) return true;
return response.json ? response.json() : false;
}
export default api;
Now, in response header I've "x-expires-at" and I want to use it in autologout. So, that if api call is made the user token resets.
auto-lougout.js
import { userService, api } from '../services';
// import { userService, api, onResponse } from '../services'; tried this as well
export default class AutoLogout {
constructor() {
super();
if (!userService.getUser()) userService.logout();
// here I am not able to call onResponse() from api.js
// getting response received from onResponse()
api.onResponse((resp) => { console.log(resp.headers.get('x-expires-at'))});
}
}
Trying to implement as an example given in this article:
https://zpao.com/posts/calling-an-array-of-functions-in-javascript/
Here I cannot use export { api, onResponse }; as api is already being used at multiple places in whole project.
How do I call onResponse function in one js file from another class in another js file ? Am I using callback correctly here ? If not, how to use callback correctly in such scenario ?
Here I cannot use export { api, onResponse }; as api is already being used at multiple places in whole project.
The correct import/export syntax for your project would be
// api.js
export function onResponse() { … }
export default function api() { … }
// index.js
export { default as userService } from './userService';
export { default as api, onResponse } from './api';
// elsewhere
import { userService, api, onResponse } from '../services';
// use these three
Am I using callback correctly here?
No, not at all. onResponse should not be a function declared in your api.js file, and it should not be exported from there - sparing you all the above hassle.
If not, how to use callback correctly in such scenario?
Make the callback a parameter of the function that uses it:
export default async function api(options, onResponse) {
// ^^^^^^^^^^
const settings = Object.assign(…);
const response = await window.fetch(`/api/v0${options.endpoint}`, settings);
onResponse(response);
…
}
Then at the call of the api function, pass your callback as an argument:
api(options, resp => {
console.log(resp.headers.get('x-expires-at'));
});

Categories

Resources