feat: Add chained invoke functions in commands and subscriptions
sebastianwessel opened this issue · comments
Currently, the context of commands and subscriptions provides the invoke
function.
Current
Currently, the builders do not have any information about invocations.
This lack of information is one of the blockers to providing tooling for deployment config generation (like generation of cloud provider permissions in deployment configs).
Also, the business code becomes quickly cluttered, typescript types are suboptimal, and testing is a lot of code overhead.
As an example:
.setCommandFunction(async function (context, payload, _parameter) {
const resultA = await context.invoke<OutputTypeA, InputPayloadTypeA, InputParameterTypeA>(
{ serviceName: 'ServiceA', serviceVersion: '1', serviceTarget: 'foo' },
payloadA,
parameterA,
)
const resultB = await context.invoke<OutputTypeB, InputPayloadTypeB, InputParameterTypeB>(
{ serviceName: 'ServiceB', serviceVersion: '1', serviceTarget: 'bar' },
payloadB,
parameterB,
)
})
Which can be mocked in tests with
const mockedPayload: PayloadType = { ... }
const mockedParameter: ParameterType = { ... }
const context = getCommandContextMock(mockedPayload, mockedParameter, sandbox)
context.stubs.invoke..callsFake((address: EBMessageAddress) => {
if(address.serviceName === 'ServiceA') {
return mockedResponseA
}
return mockedResponseB
})
New
The new behavior should be chained, and the builders should get and provide information about invocations.
// with response zod schema validation - useful for external untrusted/unknown
.canInvoke<InputPayloadTypeA, InputParameterTypeA>('ServiceA', '1', 'foo', responseOutputSchema)
// with type definition only - without runtime schema validation
.canInvoke<InputPayloadTypeB, InputParameterTypeB, OutputTypeB>('ServiceA', '1', 'bar')
.setCommandFunction(async function (context, payload, _parameter) {
// short & types are set in builder via type definition
const resultA = await context.service.ServiceA['1'].foo(payloadA, parameterA)
// short & types are set in builder from given zod schema
const resultB = await context.service.ServiceB['1'].foo(payloadB, parameterB)
})
Which can be mocked in tests with
const mockedPayload: PayloadType = { ... }
const mockedParameter: ParameterType = { ... }
const context = currentCommandBuilder.getCommandContextMock(mockedPayload, mockedParameter, sandbox)
context.stubs.service.ServiceA['1'].foo.resolves(mockedResponseA)
context.stubs.service.ServiceB['1'].bar.resolves(mockedResponseB)
Relates to "enhance the enrichment of meta-information in builders" #134