Skip to content

Commit

Permalink
multiChain and addChains tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mccraigmccraig committed Nov 15, 2023
1 parent 70630e1 commit adcf8fb
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 30 deletions.
53 changes: 28 additions & 25 deletions src/multi_chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,6 @@ import { UnionFromTuple } from "./object_builders.ts"
import { Tagged } from "./tagged.ts"
import { UPObjectChain, ObjectChainsInputU } from "./object_chain.ts"

// to type the multi-chain handler, need something like
// a conditional type which will look up return types from the program map object

// takes a list of EventHandlerPrograms and builds a new program which handles any of the events
// handled by the individual programs
// {
// // get output types for any tag
// eventPrograms: {eventTag: program}
// program: (ev) => Effect...
// }
// the event type input to the program will be the union of all the handled event types,
// while the Effect types will be chosen based on the type of the event - basically the type
// of the individual handler program for that event
//
// maybe want a MultiEventHandlerProgram type capturing the above ... and
// which would be composable
//
// the returned program will have type:
// Effect<union-of-requirements-of-programs,
// union-of-errors-of-programs,
// union-of-output-types-of-programs>

export type ProgramDeps<T extends UPObjectChain> = ReturnType<T['program']> extends Effect.Effect<infer R, infer _E, infer _V>
? R
Expand Down Expand Up @@ -68,11 +47,11 @@ export type DistributeObjectChainValueTypes<I extends Tagged, Chains extends rea
//
// the Effect result type will be narrowed to the union member corresponding
// to the input type when the input is supplied
export function multiChain<Chains extends readonly [...UPObjectChain[]]>
export function multiChainProgram<Chains extends readonly [...UPObjectChain[]]>

(eventHandlerPrograms: readonly [...Chains]) {
(chains: readonly [...Chains]) {

const progsByEventTag = eventHandlerPrograms.reduce(
const progsByEventTag = chains.reduce(
(m, p) => { m[p.tagStr] = p; return m },
{} as { [index: string]: UPObjectChain })

Expand All @@ -91,4 +70,28 @@ export function multiChain<Chains extends readonly [...UPObjectChain[]]>
} else
throw "NoProgram for tag: " + i.tag
}
}
}

export type MultiChain<Chains extends readonly [...UPObjectChain[]]> = {
readonly chains: Chains
readonly program: <Input extends ObjectChainsInputU<Chains>>(i: Input) => Effect.Effect<ProgramsDepsU<Chains>,
ProgramsErrorsU<Chains>,
Extract<DistributeObjectChainValueTypes<Input, Chains>, Input>>
}

export function multiChain<Chains extends readonly [...UPObjectChain[]]>
(chains: Chains) {

return {
chains: chains,
program: multiChainProgram(chains)
} as MultiChain<Chains>
}

export function addChains<Chains extends readonly [...UPObjectChain[]],
AdditionalChains extends readonly [...UPObjectChain[]]>
(mc: MultiChain<Chains>,
additionalChains: AdditionalChains) {

return multiChain([...mc.chains, ...additionalChains])
}
45 changes: 40 additions & 5 deletions src/multi_chain_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Effect, Context } from "effect"
import { Org, OrgService, getOrgByNick, User, UserService, getUserByIds, PushNotificationService, sendPush } from "./test_services.ts"
import { tag } from "./tagged.ts"
import { objectChain } from "./object_chain.ts"
import { multiChain } from "./multi_chain.ts"
import { multiChainProgram, multiChain, addChains } from "./multi_chain.ts"

//////////////////// some steps //////////////////////////////////

Expand Down Expand Up @@ -85,13 +85,13 @@ const echoContext = Context.empty().pipe(

const programs = [getOrgProg, sendWelcomePushProg] as const

const multiChainProgram = multiChain(programs)
const prog = multiChainProgram(programs)

Deno.test("makeHandlerProgram", () => {
Deno.test("multiChainProgram", () => {
const getOrgInput: GetOrgInput = { tag: "GetOrg", data: { org_nick: "foo" } }

// note the inferred Effect value type selects the output of the getOrg chain
const getOrgEffect = multiChainProgram(getOrgInput)
const getOrgEffect = prog(getOrgInput)
const getOrgRunnable = Effect.provide(getOrgEffect, echoContext)
const getOrgResult = Effect.runSync(getOrgRunnable)

Expand All @@ -104,7 +104,7 @@ Deno.test("makeHandlerProgram", () => {
const sendWelcomePushInput: SendWelcomePushInput = { tag: "SendWelcomePush", data: { org_nick: "foo", user_id: "100" } }

// note the inferred Effect value type selects the output of the sendWelcomePush chain
const sendWelcomePushEffect = multiChainProgram(sendWelcomePushInput)
const sendWelcomePushEffect = prog(sendWelcomePushInput)
const sendWelcomePushRunnable = Effect.provide(sendWelcomePushEffect, echoContext)
const sendWelcomePushResult = Effect.runSync(sendWelcomePushRunnable)

Expand All @@ -115,4 +115,39 @@ Deno.test("makeHandlerProgram", () => {
formatWelcomePush: "Welcome Bar of Foo",
sendPush: "push sent OK: Welcome Bar of Foo"
})
})

Deno.test("multiChain", () => {
const mc = multiChain(programs)

const getOrgInput: GetOrgInput = { tag: "GetOrg", data: { org_nick: "foo" } }

// note the inferred Effect value type selects the output of the getOrg chain
const getOrgEffect = mc.program(getOrgInput)
const getOrgRunnable = Effect.provide(getOrgEffect, echoContext)
const getOrgResult = Effect.runSync(getOrgRunnable)

assertEquals(getOrgResult, {
...getOrgInput,
org: { id: "foo", name: "Foo" },
apiResponse: { org: { id: "foo", name: "Foo" } }
})
})

Deno.test("addChains", () => {
const emptyMultiChain = multiChain([])
const mc = addChains(emptyMultiChain, programs)

const getOrgInput: GetOrgInput = { tag: "GetOrg", data: { org_nick: "foo" } }

// note the inferred Effect value type selects the output of the getOrg chain
const getOrgEffect = mc.program(getOrgInput)
const getOrgRunnable = Effect.provide(getOrgEffect, echoContext)
const getOrgResult = Effect.runSync(getOrgRunnable)

assertEquals(getOrgResult, {
...getOrgInput,
org: { id: "foo", name: "Foo" },
apiResponse: { org: { id: "foo", name: "Foo" } }
})
})

0 comments on commit adcf8fb

Please sign in to comment.