From d7787cf1c6041305c6e48c35112c7a9790a59772 Mon Sep 17 00:00:00 2001 From: Alejo Thomas Ortega Date: Wed, 12 Jul 2023 12:19:46 -0300 Subject: [PATCH] fix: support .buffer method on web environments (#49) --- src/environment.ts | 3 +++ src/fetcher.ts | 10 ++++++++++ test/fetch.spec.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 src/environment.ts diff --git a/src/environment.ts b/src/environment.ts new file mode 100644 index 0000000..55d129a --- /dev/null +++ b/src/environment.ts @@ -0,0 +1,3 @@ +export function isUsingNode(): boolean { + return typeof process !== 'undefined' && process.release && process.release.name === 'node' +} diff --git a/src/fetcher.ts b/src/fetcher.ts index 936b70f..2b6428a 100644 --- a/src/fetcher.ts +++ b/src/fetcher.ts @@ -1,6 +1,7 @@ import * as crossFetch from 'cross-fetch' import { IFetchComponent, RequestOptions, Request, Response } from '@well-known-components/interfaces' import { FetcherOptions } from './types' +import { isUsingNode } from './environment' const NON_RETRYABLE_STATUS_CODES = [400, 401, 403, 404] const IDEMPOTENT_HTTP_METHODS = ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE'] @@ -85,6 +86,15 @@ export function createFetchComponent(defaultOptions?: FetcherOptions): IFetchCom throw new Error(`Failed to fetch ${url}. Got status ${response?.status}. Response was '${responseText}'`) } + if (!isUsingNode() && !!response) { + Object.defineProperty(response, 'buffer', { + value: async function (): Promise { + return Buffer.from(await response!.arrayBuffer()) + }, + configurable: true + }) + } + // Parse response in case of abortion return signal.aborted ? undefined : (response as any) } diff --git a/test/fetch.spec.ts b/test/fetch.spec.ts index 255800d..600c129 100644 --- a/test/fetch.spec.ts +++ b/test/fetch.spec.ts @@ -1,5 +1,6 @@ import { IFetchComponent } from '@well-known-components/interfaces' import { createFetchComponent } from './../src/fetcher' +import * as environment from '../src/environment' import fetchMock from 'jest-fetch-mock' @@ -331,4 +332,46 @@ describe('fetchComponent', () => { expect(fetchMock).toHaveBeenCalledTimes(1) }) + + it('should expose .buffer function even when it is not called from a Node environment', async () => { + const expectedResponseBody = { mock: 'successful', ok: true } + jest.spyOn(environment, 'isUsingNode').mockReturnValue(false) + + fetchMock.mockResolvedValue( + new Response(JSON.stringify(expectedResponseBody), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }) + ) + + const response = await sut.fetch('https://example.com', { + attempts: 3, + retryDelay: 10 + } as any) + + expect(response).toBeDefined() + expect(response.buffer).toBeDefined() + }) + + it('should read buffer correctly from .buffer function even when it is not called from a Node environment', async () => { + const expectedResponseBody = { mock: 'successful', ok: true } + jest.spyOn(environment, 'isUsingNode').mockReturnValue(false) + + fetchMock.mockResolvedValue( + new Response(JSON.stringify(expectedResponseBody), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }) + ) + + const response = await sut.fetch('https://example.com', { + attempts: 3, + retryDelay: 10 + } as any) + + const bufferedResponse = await response.buffer() + + const parsedBufferedResponse = JSON.parse(bufferedResponse.toString()) + expect(parsedBufferedResponse).toEqual(expectedResponseBody) + }) })