Skip to content
23 changes: 20 additions & 3 deletions src/api/request-fetcher/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ it('parses application/json with charset', async () => {
url: 'http://test.com/endpoint',
method: 'GET'
})
).toEqual({body: {num: 12345}, bodyType: 'json', status: 200})
).toEqual({
body: {num: 12345},
bodyType: 'json',
status: 200,
headers: new Headers({'content-type': 'application/json; charset=utf-8'})
})
})

it('does not parse the response body if it cant determine the response type', async () => {
Expand All @@ -32,7 +37,14 @@ it('does not parse the response body if it cant determine the response type', as
url: 'http://test.com/endpoint',
method: 'GET'
})
).toEqual({status: 200, body: null, bodyType: null})
).toEqual({
status: 200,
body: null,
bodyType: null,
headers: new Headers({
'Content-Type': ''
})
})

// unknown content type
fetchMock.mockResponseOnce(JSON.stringify({num: 12345}), {
Expand All @@ -43,7 +55,12 @@ it('does not parse the response body if it cant determine the response type', as
url: 'http://test.com/endpoint',
method: 'GET'
})
).toEqual({status: 200, body: null, bodyType: null})
).toEqual({
status: 200,
body: null,
bodyType: null,
headers: new Headers({'Content-Type': 'unknown'})
})
})

it('throws a TypeError if an invalid body is passed', async () => {
Expand Down
10 changes: 4 additions & 6 deletions src/api/request-fetcher/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,14 @@ export class ApiRequestFetcher implements RequestFetcher {
response: Response,
params: RequestFetcherParams
): Promise<RequestFetcherResponse> {
const {status} = response
const {status, headers} = response

const responseType =
params.responseType ||
this.inferResponseTypeUsingContentType(
response.headers.get('Content-Type')
)
this.inferResponseTypeUsingContentType(headers.get('Content-Type'))

if (!responseType) {
return {body: null, bodyType: null, status}
return {body: null, bodyType: null, status, headers}
}

const body = await (() => {
Expand All @@ -56,7 +54,7 @@ export class ApiRequestFetcher implements RequestFetcher {
}
})()

return {body: body, bodyType: responseType, status}
return {body: body, bodyType: responseType, status, headers}
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/api/request-manager/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ export class ApiRequestManager {
successCodes: params.successCodes
})

if (params.method === 'HEAD') {
return response.headers
}

const parsedResponse: RequestFetcherResponse = {
...response,
body:
Expand Down
16 changes: 13 additions & 3 deletions src/api/typings.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
export type ApiRequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
export type ApiRequestMethod =
| 'GET'
| 'HEAD'
| 'POST'
| 'PUT'
| 'PATCH'
| 'DELETE'
export type ApiHeaders = Record<string, string>
type GetDeleteRequestMethod = Extract<ApiRequestMethod, 'GET' | 'DELETE'>
type GetDeleteRequestMethod = Extract<
ApiRequestMethod,
'GET' | 'HEAD' | 'DELETE'
>
type PostPutPatchRequestMethod = Extract<
ApiRequestMethod,
'POST' | 'PUT' | 'PATCH'
>
export type RequestBody = BodyInit | object
export type ResponseBody = Blob | object | string
export type ResponseBody = Blob | object | string | Headers
export type ApiResponseType = 'json' | 'text' | 'blob'

interface ApiCommonRequestParams<TMethod extends ApiRequestMethod> {
Expand Down Expand Up @@ -110,4 +119,5 @@ export interface RequestFetcherResponse {
body: ResponseBody | null
bodyType: ApiResponseType | null
status: number
headers: Headers
}
16 changes: 10 additions & 6 deletions src/endpoints/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ describe('HttpEndpoints', () => {
class Endpoints extends HttpEndpoints {
static basePath = '/base'

static HEAD(init: Omit<EndpointCreateRequestInit, 'body'>) {
return super._head('/endpoint', init)
}

static GET(init: Omit<EndpointCreateRequestInit, 'body'>) {
return super._get('/endpoint', init)
}
Expand All @@ -31,7 +35,7 @@ describe('HttpEndpoints', () => {
}

test('endpoints by method', () => {
for (const method of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
for (const method of ['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
expect(Endpoints[method]()).toEqual({
method,
url: '/base/endpoint'
Expand All @@ -54,7 +58,7 @@ describe('HttpEndpoints', () => {
test('request headers', () => {
const headers = {test: 'body'}

for (const method of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
for (const method of ['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
expect(Endpoints[method]({headers})).toEqual({
method,
url: '/base/endpoint',
Expand All @@ -73,7 +77,7 @@ describe('HttpEndpoints', () => {
zero: 0
}

for (const method of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
for (const method of ['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
expect(Endpoints[method]({query})).toEqual({
method,
url: '/base/endpoint?empty=&null=null&num=3&str=string&zero=0'
Expand All @@ -85,7 +89,7 @@ describe('HttpEndpoints', () => {
})

test('extra key', () => {
for (const method of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
for (const method of ['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
expect(Endpoints[method]({extraKey: 'test'})).toEqual({
method,
url: '/base/endpoint',
Expand All @@ -98,7 +102,7 @@ describe('HttpEndpoints', () => {
test('applying trailing slash to each url', () => {
;(Endpoints as any).trailingSlash = true

for (const method of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
for (const method of ['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
expect(Endpoints[method]()).toEqual({
method,
url: '/base/endpoint/'
Expand All @@ -111,7 +115,7 @@ describe('HttpEndpoints', () => {
test('applying trailing slash to each url', () => {
;(Endpoints as any).trailingSlash = true

for (const method of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
for (const method of ['HEAD', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
expect(Endpoints[method]()).toEqual({
method,
url: '/base/endpoint/'
Expand Down
13 changes: 13 additions & 0 deletions src/endpoints/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ export class HttpEndpoints {
return builtPath
}

/**
* Creates a `HEAD` request.
*
* @param path A path relative to the base path.
* @param requestInit Additional request parameters.
*/
protected static _head<TResponseBody extends ResponseBody = ResponseBody>(
path: string,
requestInit?: Omit<EndpointCreateRequestInit, 'body'>
) {
return this._createRequest<'HEAD', TResponseBody>('HEAD', path, requestInit)
}

/**
* Creates a `GET` request.
*
Expand Down