-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
fetchData
to utils package
- improve the code - add unit test
- Loading branch information
1 parent
cfe281a
commit f27467a
Showing
2 changed files
with
314 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
/* eslint-disable no-undef */ | ||
import { fetchData } from './index'; | ||
|
||
const url = 'https://api.example.com/graphql'; | ||
const query = ` | ||
query { | ||
users { | ||
id | ||
name | ||
} | ||
} | ||
`; | ||
|
||
describe('fetchData', () => { | ||
it('should fetch data successfully', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
json: () => Promise.resolve({ data: { users: [] } }), | ||
ok: true, | ||
status: 200, | ||
statusText: 'OK', | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
|
||
const data = await fetchData({ url, query }); | ||
expect(data).toEqual({ data: { users: [] } }); | ||
expect(mockFetch).toHaveBeenCalledWith(url, { | ||
method: 'POST', | ||
cache: 'no-store', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ query }), | ||
}); | ||
}); | ||
it('should have POST method by default', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
json: () => Promise.resolve({ data: { users: [] } }), | ||
ok: true, | ||
status: 200, | ||
statusText: 'OK', | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
const data = await fetchData({ url, query }); | ||
expect(data).toEqual({ data: { users: [] } }); | ||
expect(mockFetch).toHaveBeenCalledWith(url, { | ||
method: 'POST', | ||
cache: 'no-store', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ query }), | ||
}); | ||
}); | ||
it('should handle GET method', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
json: () => Promise.resolve({ data: { users: [] } }), | ||
ok: true, | ||
status: 200, | ||
statusText: 'OK', | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
const data = await fetchData({ url: 'https://example.com/api/users', method: 'GET' }); | ||
expect(data).toEqual({ data: { users: [] } }); | ||
expect(mockFetch).toHaveBeenCalledWith('https://example.com/api/users', { | ||
method: 'GET', | ||
cache: 'no-store', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
}); | ||
}); | ||
it('should handle cache', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
json: () => Promise.resolve({ data: { users: [] } }), | ||
ok: true, | ||
status: 200, | ||
statusText: 'OK', | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
|
||
const data = await fetchData({ url, query }); | ||
expect(data).toEqual({ data: { users: [] } }); | ||
expect(mockFetch).toHaveBeenCalledWith(url, { | ||
cache: 'no-store', | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ query }), | ||
}); | ||
}); | ||
it('should handle network error', async () => { | ||
const mockFetch = jest.fn(() => Promise.reject(new Error('Network error'))); | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Network error'); | ||
}); | ||
it('should handle error', async () => { | ||
const mockFetch = jest.fn(() => Promise.reject(new Error('Fetch failed'))); | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow(); | ||
}); | ||
it('should handle error with status code', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
json: () => Promise.resolve({ errors: [{ message: 'Not found' }] }), | ||
ok: false, | ||
status: 404, | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
|
||
expect(fetchData({ url, query })).rejects.toThrow('Not found'); | ||
}); | ||
it('should handle error with status code 400', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
json: () => Promise.resolve({ errors: [{ message: 'Bad request' }] }), | ||
ok: false, | ||
status: 400, | ||
statusText: 'Bad request', | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Bad request'); | ||
}); | ||
it('should handle error with status code 403', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
json: () => Promise.resolve({ errors: [{ message: 'Forbidden' }] }), | ||
ok: false, | ||
status: 403, | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Forbidden'); | ||
}); | ||
it('should handle error with status code 404', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
ok: false, | ||
status: 404, | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Not found'); | ||
}); | ||
it('should handle error with status code 422', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
ok: false, | ||
status: 422, | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Unprocessable entity'); | ||
}); | ||
it('should handle error with status code 500', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
ok: false, | ||
status: 500, | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Internal server error'); | ||
}); | ||
it('should handle error with status code 503', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
ok: false, | ||
status: 503, | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Service unavailable'); | ||
}); | ||
it('should handle error with status code 504', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
ok: false, | ||
status: 504, | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Gateway timeout'); | ||
}); | ||
it('should handle error with status code 505', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
ok: false, | ||
status: 505, | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('HTTP version not supported'); | ||
}); | ||
it('should handle error with custom status code', async () => { | ||
const mockFetch = jest.fn(() => | ||
Promise.resolve({ | ||
ok: false, | ||
status: 600, | ||
statusText: 'Unknown error', | ||
}), | ||
) as jest.Mock; | ||
global.fetch = mockFetch; | ||
expect(fetchData({ url, query })).rejects.toThrow('Unknown error'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { ErrorHandler } from '../errorHandler'; | ||
|
||
interface HandleGraphqlRequestProps { | ||
query?: string; | ||
variables: any; | ||
} | ||
|
||
const handleGraphqlRequest = ({ query, variables }: HandleGraphqlRequestProps) => | ||
({ | ||
method: 'POST', | ||
headers: { 'Content-Type': 'application/json' }, | ||
body: JSON.stringify({ query, variables }), | ||
cache: 'no-store', | ||
}) as RequestInit; | ||
|
||
export interface FetchDataProps { | ||
url: string; | ||
query?: string; | ||
variables?: any; | ||
method?: string; | ||
} | ||
|
||
/** | ||
* @description This function is used to fetch data from the server | ||
* @param {string} url - The url to fetch data from | ||
* @param {string} query - The query to fetch data from the GraphQL API server | ||
* @param {any} variables - The variables to pass it to the GraphQL Queries | ||
* @param {string} method - The method to use for fetching data, default is POST | ||
* @returns {Promise<T>} - The data fetched from the server | ||
* @example | ||
* GraphQL Example: | ||
* const data = await fetchData<Users>({ url: 'https://example.com/graphql', query: 'query { users { id name } }' }); | ||
* API Example: | ||
* const data = await fetchData<Users>({ url: 'https://example.com/api', method: 'GET' }); | ||
* */ | ||
// The POST method is set as the default option because it is commonly used to fetch data from GraphQL. | ||
export const fetchData = async <T>({ url, query, variables, method = 'POST' }: FetchDataProps): Promise<T> => { | ||
const defaultOptions = { | ||
method, | ||
cache: 'no-store', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
} as RequestInit; | ||
const graphqlOption = handleGraphqlRequest({ query, variables }); | ||
try { | ||
const response = await fetch(url, query ? graphqlOption : defaultOptions); | ||
if (!response.ok) { | ||
const { logger } = new ErrorHandler(); | ||
logger(); | ||
switch (response.status) { | ||
case 400: | ||
throw new ErrorHandler(response.statusText, { | ||
statusCode: 400, | ||
}); | ||
case 403: | ||
throw new ErrorHandler('Forbidden', { | ||
statusCode: 403, | ||
}); | ||
case 404: | ||
throw new ErrorHandler('Not found', { | ||
statusCode: 404, | ||
}); | ||
case 422: | ||
throw new ErrorHandler('Unprocessable entity', { | ||
statusCode: 422, | ||
}); | ||
case 500: | ||
throw new ErrorHandler('Internal server error', { | ||
statusCode: 500, | ||
}); | ||
case 503: | ||
logger(); | ||
throw new ErrorHandler('Service unavailable', { | ||
statusCode: 503, | ||
}); | ||
case 504: | ||
throw new ErrorHandler('Gateway timeout', { | ||
statusCode: 504, | ||
}); | ||
case 505: | ||
throw new ErrorHandler('HTTP version not supported', { | ||
statusCode: 505, | ||
}); | ||
default: | ||
throw new ErrorHandler(response.statusText, { | ||
statusCode: response.status, | ||
}); | ||
} | ||
} | ||
const data = await response.json(); | ||
return data; | ||
} catch (error: any) { | ||
throw new ErrorHandler(error?.message, { | ||
statusCode: error?.options?.statusCode, | ||
}); | ||
} | ||
}; |