From 27ac9ba48974d495a2674f82eeaaa75327b9fda9 Mon Sep 17 00:00:00 2001 From: Tom Howard Date: Mon, 27 Jun 2022 11:44:56 +1000 Subject: [PATCH] feat(content-parser): content parsers can now access previous implementations --- src/augment-options.ts | 10 +++++--- src/test/operations.steps.ts | 7 +++--- src/waychaser.ts | 48 ++++++++++++++++++++---------------- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/augment-options.ts b/src/augment-options.ts index a3a4ead9..787ca8a6 100644 --- a/src/augment-options.ts +++ b/src/augment-options.ts @@ -12,12 +12,14 @@ export function augmentOptions( handlers: baseHandlers, defaultHandlers: baseDefaultHandlers, headers: baseHeaders, + contentParser: baseContentParser, ...baseOther } = baseOptions const { handlers: additionalHandlers, defaultHandlers: additionalDefaultHandlers, headers: additionalHeaders, + contentParser: additionalContentParser, ...additionalOther } = additionalOptions || {} @@ -32,7 +34,9 @@ export function augmentOptions( ...(additionalDefaultHandlers || baseDefaultHandlers || []) ]) - const finalOptions = { + const mergedContentParser = additionalContentParser ? (response: Response) => additionalContentParser(response, baseContentParser) : baseContentParser + + return { ...baseOther, ...additionalOther, defaultHandlers: mergedHandlers, @@ -40,7 +44,7 @@ export function augmentOptions( headers: { ...baseHeaders, ...additionalHeaders - } + }, + contentParser: mergedContentParser } - return finalOptions } diff --git a/src/test/operations.steps.ts b/src/test/operations.steps.ts index a72b94b7..d09c8489 100644 --- a/src/test/operations.steps.ts +++ b/src/test/operations.steps.ts @@ -2,7 +2,7 @@ import { expect } from 'chai' import { DataTable } from '@cucumber/cucumber' import logger from './logger' import { binding, given, then, when } from 'cucumber-tsflow'; -import { Validator, waychaser, WayChaserProblem, WayChaserResponse } from '../waychaser' +import { ContentParser, Validator, waychaser, WayChaserProblem, WayChaserResponse } from '../waychaser' import { uniqueNamesGenerator, adjectives, @@ -908,13 +908,12 @@ export class OperationSteps { @given('waychaser has a custom parser for 404s') public async waychaserHasACustomParserFor404s() { - const existingContentParser = this.waychaser.currentDefaults.contentParser; this.waychaser = this.waychaser.defaults({ - contentParser: async (response: Response) => { + contentParser: async (response: Response, contentParser: ContentParser) => { return response.status === 404 ? new ProblemDocument({ 'title': "Not found", 'type': 'https://waychaser.io/not-found' - }) : existingContentParser(response); + }) : contentParser(response); } }) }; diff --git a/src/waychaser.ts b/src/waychaser.ts index dc16e314..106d09fb 100644 --- a/src/waychaser.ts +++ b/src/waychaser.ts @@ -333,6 +333,8 @@ export type HandlerSpec = { export type Stopper = () => void +export type ContentParser = (response: Response, contentParser?: ContentParser) => Promise + export type WayChaserOptions = RequestInit & { fetch: typeof fetch handlers: Array @@ -340,7 +342,7 @@ export type WayChaserOptions = RequestInit & { preInterceptors: Array<(uriOrRequest, updateOptions, stopper) => void> postInterceptors: Array<(response: (WayChaserResponse), stop: Stopper) => void> postErrorInterceptors: Array<(error: WayChaserProblem | Error, stopper: Stopper) => void> - contentParser: (response: Response) => Promise + contentParser: ContentParser parameters?: Record validator?: Validator } @@ -458,27 +460,31 @@ export async function _waychaser( updateOptions ) - // TODO allow lazy loading of the content - const content = await mergedOptions.contentParser(baseResponse) - // content is - // 1. unknown, - // 2. a post response client side Problem Document, - // 3. a server side ProblemDocument, or - // 4. undefined - - try { - const response = WayChaserResponse.create(baseResponse, content, defaultOptions, mergedOptions) - - stop = false - for (const interceptor of updateOptions.postInterceptors) { - interceptor(response, () => (stop = true)) - if (stop) { - break + try { + // TODO allow lazy loading of the content + const content = await mergedOptions.contentParser(baseResponse) + // content is + // 1. unknown, + // 4. undefined + const response = WayChaserResponse.create(baseResponse, content, defaultOptions, mergedOptions) + + stop = false + for (const interceptor of updateOptions.postInterceptors) { + interceptor(response, () => (stop = true)) + if (stop) { + break + } } + return response + } + catch(error){ + throw error instanceof ProblemDocument ? new WayChaserProblem({ + response: new WayChaserResponse({ handlers: mergedOptions.defaultHandlers, defaultOptions, baseResponse, content: error, parameters: mergedOptions.parameters }), + problem: error, + }) : error; } - return response - } catch (error) { + } catch(error) { stop = false for (const interceptor of updateOptions.postErrorInterceptors) { interceptor(error, () => (stop = true)) @@ -524,7 +530,7 @@ const wayChaserDefaults: WayChaserOptions = { return contentType === 'application/problem+json' ? Object.assign(new ProblemDocument(jsonContent), jsonContent) : jsonContent; } catch (error) { - return new ProblemDocument({ + throw new ProblemDocument({ type: "https://waychaser.io/invalid-json", title: "JSON response could not be parsed", detail: `The response document with content type '${contentType}' could not be parsed as json`, @@ -532,7 +538,7 @@ const wayChaserDefaults: WayChaserOptions = { }) } } - return new ProblemDocument({ + throw new ProblemDocument({ type: "https://waychaser.io/unsupported-content-type", title: "The response has an unsupported content type", detail: `The response document has a content type of '${contentType}' which is not supported`,