From 43dea74bc9ad3651b93207f57583305f58839427 Mon Sep 17 00:00:00 2001 From: Rasmus Gustafsson Date: Thu, 21 Dec 2023 21:13:43 +0200 Subject: [PATCH] feat: make functions chainable Also fixed type bugs, and made it so that params are required when adding or overriding events, explanations or solutions. Closes #6 --- src/index.ts | 81 ++++++++++++++++++++++++++++++++-------------- test/logs.test.ts | 32 ++++++++++++++++++ test/types.test.ts | 22 +++++++++++++ 3 files changed, 111 insertions(+), 24 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1209a68..07e7bc7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -71,12 +71,12 @@ export function createHumanLogs(options: Huma type Explanations = HumanLogs['explanations'] type Events = HumanLogs['events'] type Solutions = HumanLogs['solutions'] - type Params> = UnionToIntersection< + type ExplanationParams> = UnionToIntersection< { [I in keyof K]: Explanations[K[I]] extends { params: infer P } ? P : never }[number] > - type EventsParams> = UnionToIntersection< + type EventParams> = UnionToIntersection< { [I in keyof K]: Events[K[I]] extends { params: infer P } ? P : never }[number] @@ -100,42 +100,50 @@ export function createHumanLogs(options: Huma events?: Events[] explanations?: Explanations[] solutions?: Solutions[] - params?: Params & EventsParams & SolutionParams + params?: ExplanationParams & EventParams & SolutionParams }) { const eventParts: string[] = [] const explanationParts: string[] = [] const solutionParts: string[] = [] const actionParts: ActionType[] = [] - const addEventParts = (events: Events[]) => { + const addEventParts = (events: Events[], overwrittenParams?: HumanLogs['params']) => { events.forEach((event) => { eventParts.push( - ...getMessagePartsFor('events', options, event as string, params as HumanLogs['params']) + ...getMessagePartsFor( + 'events', + options, + event as string, + overwrittenParams ?? (params as HumanLogs['params']) + ) ) }) } - const addExplanationParts = (explanations: Explanations[]) => { + const addExplanationParts = ( + explanations: Explanations[], + overwrittenParams?: HumanLogs['params'] + ) => { explanations?.forEach((explanation) => { explanationParts.push( ...getMessagePartsFor( 'explanations', options, explanation as string, - params as HumanLogs['params'] + overwrittenParams ?? (params as HumanLogs['params']) ) ) }) } - const addSolutionParts = (solutions: Solutions[]) => { + const addSolutionParts = (solutions: Solutions[], overwrittenParams?: HumanLogs['params']) => { solutions?.forEach((solution) => { solutionParts.push( ...getMessagePartsFor( 'solutions', options, solution as string, - params as HumanLogs['params'] + overwrittenParams ?? (params as HumanLogs['params']) ) ) @@ -164,33 +172,58 @@ export function createHumanLogs(options: Huma return [...eventParts, ...explanationParts, ...solutionParts].join(' ') }, // Adds - addEvents(events: Events[]) { - addEventParts(events) + addEvents( + events: InnerEvents[], + params?: EventParams + ) { + addEventParts(events as unknown as Events[], params as HumanLogs['params']) + return this }, - addExplanations(explanations: Explanations[]) { - addExplanationParts(explanations) + addExplanations( + explanations: InnerExplanations[], + params?: ExplanationParams + ) { + addExplanationParts(explanations as unknown as Explanations[], params as any) + return this }, - addSolutions(solutions: Solutions[]) { - addSolutionParts(solutions) + addSolutions( + solutions: InnerSolutions[], + params?: SolutionParams + ) { + addSolutionParts(solutions as unknown as Solutions[], params as HumanLogs['params']) + return this }, // Overrides - overrideEvents(events: (keyof HumanLogs['events'])[]) { + overrideEvents( + events: InnerEvents[], + params?: EventParams + ) { // Clear event parts eventParts.length = 0 - // @ts-ignore Add events - addEventParts(events) + addEventParts(events as unknown as Events[], params as HumanLogs['params']) + return this }, - overrideExplanations(explanations: (keyof HumanLogs['explanations'])[]) { + overrideExplanations( + explanations: InnerExplanations[], + params?: ExplanationParams + ) { // Clear explanation parts explanationParts.length = 0 - // @ts-ignore Add explanations - addExplanationParts(explanations) + addExplanationParts( + explanations as unknown as Explanations[], + params as HumanLogs['params'] + ) + return this }, - overrideSolutions(solutions: (keyof HumanLogs['solutions'])[]) { + + overrideSolutions( + solutions: InnerSolutions[], + params?: SolutionParams + ) { // Clear solution parts solutionParts.length = 0 - // @ts-ignore Add explanations - addSolutionParts(solutions) + addSolutionParts(solutions as unknown as Solutions[], params as HumanLogs['params']) + return this }, toString() { return `${[...eventParts, ...explanationParts, ...solutionParts].join(' ')}${ diff --git a/test/logs.test.ts b/test/logs.test.ts index d1f36b7..4396470 100644 --- a/test/logs.test.ts +++ b/test/logs.test.ts @@ -76,6 +76,20 @@ it('can override events, explanations & solutions', () => { ) }) +it('can add & override events with params', () => { + const humanLog = mockHumanLogs({ + events: ['team_create_failed'] + }) + + humanLog.addExplanations(['team_exists'], { + teamId: 'flytrap' + }) + + expect(humanLog.message).toStrictEqual( + 'Creating your team failed because a team with ID "flytrap" already exists.' + ) +}) + it('smoke test', () => { const humanLog = mockHumanLogs({ events: ['project_create_failed'], @@ -97,3 +111,21 @@ it('smoke test', () => { } ]) }) + +it('can chain functions', () => { + const humanLog = mockHumanLogs({ + events: ['project_create_failed'], + explanations: ['api_unreachable'], + solutions: ['check_status_page'] + }) + + expect( + humanLog + .addExplanations(['team_exists'], { + teamId: 'flytrap' + }) + .addSolutions(['try_again']).message + ).toEqual( + 'Cannot create your project because the API cannot be reached. because a team with ID "flytrap" already exists. You can check the status of our services on our status page. Please try again.' + ) +}) diff --git a/test/types.test.ts b/test/types.test.ts index 8a91c68..1cfd610 100644 --- a/test/types.test.ts +++ b/test/types.test.ts @@ -31,6 +31,19 @@ describe('Type tests', () => { solutions: ['nonexistent'] }) }) + + it('adding to existing events, explanations & solutions', () => { + const humanLog = mockHumanLogs({ + explanations: ['team_exists'], + solutions: ['check_status_page'], + params: { + teamId: 'some-team' + } + }) + + humanLog.addExplanations(['api_unreachable']) + humanLog.addSolutions(['contact_us']) + }) }) describe('Params type tests', () => { @@ -78,4 +91,13 @@ describe('Params type tests', () => { } }) }) + + it('errors when adding', () => { + const humanLog = mockHumanLogs({ + events: ['project_create_failed'], + explanations: ['api_unreachable'] + }) + // @ts-expect-error: params for `team_exists` should be required + humanLog.addExplanations(['team_exists'], {}) + }) })