Skip to content

Commit

Permalink
feat: make functions chainable
Browse files Browse the repository at this point in the history
Also fixed type bugs, and made it so that params are required when adding or overriding events, explanations or solutions.

Closes #6
  • Loading branch information
skoshx committed Dec 21, 2023
1 parent 3b06d87 commit 43dea74
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 24 deletions.
81 changes: 57 additions & 24 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ export function createHumanLogs<HumanLogs extends HumanLogsObject>(options: Huma
type Explanations = HumanLogs['explanations']
type Events = HumanLogs['events']
type Solutions = HumanLogs['solutions']
type Params<K extends Array<keyof Explanations>> = UnionToIntersection<
type ExplanationParams<K extends Array<keyof Explanations>> = UnionToIntersection<
{
[I in keyof K]: Explanations[K[I]] extends { params: infer P } ? P : never
}[number]
>
type EventsParams<K extends Array<keyof Events>> = UnionToIntersection<
type EventParams<K extends Array<keyof Events>> = UnionToIntersection<
{
[I in keyof K]: Events[K[I]] extends { params: infer P } ? P : never
}[number]
Expand All @@ -100,42 +100,50 @@ export function createHumanLogs<HumanLogs extends HumanLogsObject>(options: Huma
events?: Events[]
explanations?: Explanations[]
solutions?: Solutions[]
params?: Params<Explanations[]> & EventsParams<Events[]> & SolutionParams<Solutions[]>
params?: ExplanationParams<Explanations[]> & EventParams<Events[]> & SolutionParams<Solutions[]>
}) {
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'])
)
)

Expand Down Expand Up @@ -164,33 +172,58 @@ export function createHumanLogs<HumanLogs extends HumanLogsObject>(options: Huma
return [...eventParts, ...explanationParts, ...solutionParts].join(' ')
},
// Adds
addEvents(events: Events[]) {
addEventParts(events)
addEvents<InnerEvents extends keyof HumanLogs['events']>(
events: InnerEvents[],
params?: EventParams<InnerEvents[]>
) {
addEventParts(events as unknown as Events[], params as HumanLogs['params'])
return this
},
addExplanations(explanations: Explanations[]) {
addExplanationParts(explanations)
addExplanations<InnerExplanations extends keyof HumanLogs['explanations']>(
explanations: InnerExplanations[],
params?: ExplanationParams<InnerExplanations[]>
) {
addExplanationParts(explanations as unknown as Explanations[], params as any)
return this
},
addSolutions(solutions: Solutions[]) {
addSolutionParts(solutions)
addSolutions<InnerSolutions extends keyof HumanLogs['solutions']>(
solutions: InnerSolutions[],
params?: SolutionParams<InnerSolutions[]>
) {
addSolutionParts(solutions as unknown as Solutions[], params as HumanLogs['params'])
return this
},
// Overrides
overrideEvents(events: (keyof HumanLogs['events'])[]) {
overrideEvents<InnerEvents extends keyof HumanLogs['events']>(
events: InnerEvents[],
params?: EventParams<InnerEvents[]>
) {
// 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<InnerExplanations extends keyof HumanLogs['explanations']>(
explanations: InnerExplanations[],
params?: ExplanationParams<InnerExplanations[]>
) {
// 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<InnerSolutions extends keyof HumanLogs['solutions']>(
solutions: InnerSolutions[],
params?: SolutionParams<InnerSolutions[]>
) {
// 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(' ')}${
Expand Down
32 changes: 32 additions & 0 deletions test/logs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand All @@ -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.'
)
})
22 changes: 22 additions & 0 deletions test/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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'], {})
})
})

0 comments on commit 43dea74

Please sign in to comment.