Created
February 28, 2024 19:12
-
-
Save maticzav/13dd0b9392672af75da793e7b66eee7c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ExhaustiveSwitchCheck } from '@backend/common' | |
import { IActionExecution } from './execution' | |
import { ActionDef, ActionDefPayload, ActionDefResultDict } from './utils' | |
/** | |
* A generic interface for a service that can dispatch actions. | |
*/ | |
export type IDispatcherDefinition<Definitions extends ActionDef> = { | |
[K in keyof Definitions]: (params: { | |
data: Definitions[K]['payload'] | |
execution: IActionExecution | |
}) => Promise<Definitions[K]['result']> | |
} | |
// Playground ---------------------------------------------------------------- | |
// Foo | |
export type FooActions = { | |
/** | |
* Creates a new Country and returns it. | |
*/ | |
create: { | |
payload: 'create' | |
result: 'created' | |
} | |
/** | |
* Creates multiple new Countries and returns them. | |
*/ | |
createMany: { | |
payload: 'createMany' | |
result: 'createdMany' | |
} | |
} | |
export interface IFooUpdateService extends IDispatcherDefinition<FooActions> {} | |
export class FooUpdateService implements IFooUpdateService { | |
/** | |
* Creates a new Country and returns it. | |
*/ | |
public async create({ data, execution }: { data: 'create'; execution: IActionExecution }): Promise<'created'> { | |
return 'created' | |
} | |
/** | |
* Creates multiple new Countries and returns them. | |
*/ | |
public async createMany(p: { data: 'createMany'; execution: IActionExecution }): Promise<'createdMany'> { | |
return 'createdMany' | |
} | |
} | |
type FooScope = 'foo' | |
// Bar | |
export type BarActions = { | |
/** | |
* Creates a new Country and returns it. | |
*/ | |
delete: { | |
payload: 'delete' | |
result: 'deleted' | |
} | |
/** | |
* deletes multiple new Countries and returns them. | |
*/ | |
deleteMany: { | |
payload: 'deleteMany' | |
result: 'deletedMany' | |
} | |
} | |
export interface IBarUpdateService extends IDispatcherDefinition<BarActions> {} | |
export class BarUpdateService implements IBarUpdateService { | |
/** | |
* Creates a new Country and returns it. | |
*/ | |
public async delete({ data, execution }: { data: 'delete'; execution: IActionExecution }): Promise<'deleted'> { | |
return 'deleted' | |
} | |
/** | |
* deletes multiple new Countries and returns them. | |
*/ | |
public async deleteMany(p: { data: 'deleteMany'; execution: IActionExecution }): Promise<'deletedMany'> { | |
return 'deletedMany' | |
} | |
} | |
type BarScope = 'bar' | |
// Simulate Dispatcher Service ------------------------------------------------------------------------------ | |
type ActionResult = { | |
foo: ActionDefResultDict<FooActions> | |
bar: ActionDefResultDict<BarActions> | |
} | |
/** | |
* Extracts the result type of an action based on the inferred scope and type. | |
*/ | |
type ResultOfAction<A extends Payloads> = A extends { scope: infer Scope; type: infer Type } | |
? Scope extends keyof ActionResult | |
? Type extends keyof ActionResult[Scope] | |
? ActionResult[Scope][Type] | |
: never | |
: never | |
: never | |
type Payloads = ActionDefPayload<FooScope, FooActions> | ActionDefPayload<BarScope, BarActions> | |
const fooService = new FooUpdateService() | |
const barService = new BarUpdateService() | |
async function execute<A extends Payloads>({ | |
action, | |
execution, | |
}: { | |
action: A | |
execution: IActionExecution | |
}): Promise<ResultOfAction<A>> { | |
switch (action.scope) { | |
case 'foo': { | |
const method = fooService[action.type] as (params: { | |
data: A['payload'] | |
execution: IActionExecution | |
}) => Promise<ResultOfAction<A>> | |
return method({ data: action.payload, execution }) | |
} | |
case 'bar': { | |
const method = barService[action.type] as (params: { | |
data: A['payload'] | |
execution: IActionExecution | |
}) => Promise<ResultOfAction<A>> | |
return method({ data: action.payload, execution }) | |
} | |
default: | |
throw new ExhaustiveSwitchCheck(action) | |
} | |
} | |
async function dispatch<A extends Payloads>({ action, user }: { action: A; user: {} }): Promise<ResultOfAction<A>> { | |
const execution = {} as unknown as IActionExecution | |
try { | |
const result = await execute({ action, execution }) | |
await execution.success(result) | |
return result | |
} catch (e) { | |
await execution.error(e) | |
throw e | |
} | |
} | |
const result = dispatch({ action: { scope: 'foo', type: 'create', payload: 'create' }, user: {} }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment