Skip to content

Commit

Permalink
feat: add ability to create payload to environment
Browse files Browse the repository at this point in the history
  • Loading branch information
kamaz committed Jun 14, 2020
1 parent e4b87db commit 0982bfd
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 24 deletions.
55 changes: 42 additions & 13 deletions packages/sls-env/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,58 @@
//

type PayloadConstructor<E, C, R> = (event: E, context: C) => R
// but that makes it very specific to even and context
// so maybe we extract Request to be Request {event, context}
// or maybe request becomes something abstract
// what does Request mean in context of async event
type Request<E, C> = {
event: E
context: C
type Event<I, C, O> = {
input: I
context?: C
output?: O
}

type AppConstructor<E, C, R> = ({ event, context }: { event: E; context: C }) => R | Promise<R>
// maybe application gets event, context under something different like `dependencies`
// event = <Event, Context, Response> where Event can be Request,
// event = {input, context?, output?}
// or maybe at this point we don't allow working with raw events as a good practice and abstraction
type AppConstructor<P, D, R> = ({ payload, dependencies }: { payload: P; dependencies: D }) => R | Promise<R>

// E - event
// C - context
// D - dependencies
// R - result
type SlsEnvironment<E, C, R> = {
type SlsEnvironment<E, C, D, P, R> = {
// but that may not be true as result should be handled by response
global: (func: Function) => SlsEnvironment<E, C, R>
app: (app: AppConstructor<E, C, R>) => SlsEnvironment<E, C, R>
global: (func: Function) => SlsEnvironment<E, C, D, P, R>
payload: (payloadConstructor: PayloadConstructor<E, C, P>) => SlsEnvironment<E, C, D, P, R>
app: (app: AppConstructor<P, D, R>) => SlsEnvironment<E, C, D, P, R>
start: (event: E, context: C) => Promise<R>
}

type SlsEnvironmentConfig = {}

export const environment = <E, C, R>(config?: SlsEnvironmentConfig): SlsEnvironment<E, C, R> => {
let appConstructor: AppConstructor<E, C, R>
// maybe we have really generic environment and then we have wrappers for all different scenarios?
// with this level abstraction we would need to have awsEnv
// with this level abstraction we would need to have expressEnv
// with this level abstraction we would need to have googleFunctionEnv
export const environment = <E, C, D, P, R>(config?: SlsEnvironmentConfig): SlsEnvironment<E, C, D, P, R> => {
let appConstructor: AppConstructor<P, D, R>
// at the moment we are cheating
let dependencies: D = ({} as unknown) as D
// todo: we need to have something like no payload
let payloadFactory: PayloadConstructor<E, C, P> = (event, context) => (({ event, context } as unknown) as P)
return {
// dependency
// or maybe we have something similar to app and we have a function that
// creates dependencies
// dependencies(deps().global().global().prototype().create)
// dependencies(deps().global().global().prototype())
global(func: Function) {
console.log('function name', func.name)

dependencies = { ...dependencies, [func.name]: func }
return this
},
payload(payloadConstructor) {
payloadFactory = payloadConstructor
return this
},
app(appFactory) {
Expand All @@ -43,8 +67,13 @@ export const environment = <E, C, R>(config?: SlsEnvironmentConfig): SlsEnvironm
// maybe we start currying
//
// Promise.resolve().then(appConstructor(event, context))
const invocation = { event, context }
return Promise.resolve(invocation).then(appConstructor)
const invocation = { event, context, dependencies }
return Promise.resolve(invocation)
.then(({ event, context }) => ({
payload: payloadFactory(event, context),
dependencies
}))
.then(appConstructor)
}
}
}
56 changes: 45 additions & 11 deletions packages/sls-env/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,62 @@ describe('serverless environment', () => {
type EmptyEvent = {}
type EmptyContext = {}
it('supports application', () =>
environment<EmptyEvent, EmptyContext, string>()
environment<EmptyEvent, EmptyContext, void, void, string>()
.app(() => 'hello world!')
.start({}, {})
.then(result => expect(result).toBe('hello world!')))

type MessageEvent = { message: string }
type NameContext = { name: string }
type EventPayload = { event: MessageEvent; context: NameContext }
it('supports passing an event and context to application', () =>
environment<MessageEvent, NameContext, string>()
.app(({ event, context }) => `${event.message} ${context.name}!`)
environment<MessageEvent, NameContext, void, EventPayload, string>()
.app(({ payload: { event, context } }) => `${event.message} ${context.name}!`)
.start({ message: 'hello' }, { name: 'world' })
.then(result => expect(result).toBe('hello world!')))

// it('supports adding dependencies to environment', () => {
// const buildMessage = (message: string, name: string): string => `${message} ${name}`
type BuildMessage = (message: string, name: string) => string
type BuildMessageDependencies = {
buildMessage: BuildMessage
}
it('supports adding dependencies to environment', () => {
const buildMessage: BuildMessage = (message, name) => `${message} ${name}!`

// environment<MessageEvent, NameContext, string>()
// .global(buildMessage)
// .app(({ event, context, dependencies: { buildMessage } }) => buildMessage(event.message, context.name))
// .start({ message: 'hello' }, { name: 'world' })
// .then(result => expect(result).toBe('hello world!'))
// })
return environment<MessageEvent, NameContext, BuildMessageDependencies, EventPayload, string>()
.global(buildMessage)
.app(({ payload: { event, context }, dependencies: { buildMessage } }) =>
buildMessage(event.message, context.name)
)
.start({ message: 'hello' }, { name: 'world' })
.then(result => expect(result).toBe('hello world!'))
})

it.todo('supports adding not named dependencies to environment')

it('supports creating a payload for the application', () => {
type HelloWorldMessage = {
hello: string
world: string
}

type BuildHelloWorldMessage = (message: HelloWorldMessage) => string

const buildMessage: BuildHelloWorldMessage = message => `${message.hello} ${message.world}!`

type BuildHelloWorldMessageDependencies = {
buildMessage: BuildHelloWorldMessage
}

return environment<MessageEvent, NameContext, BuildHelloWorldMessageDependencies, HelloWorldMessage, string>()
.global(buildMessage)
.payload((event, context) => ({
hello: event.message,
world: context.name
}))
.app(({ payload, dependencies: { buildMessage } }) => buildMessage(payload))
.start({ message: 'hello' }, { name: 'world' })
.then(result => expect(result).toBe('hello world!'))
})
// google run against express
// it('runs request / response environment', () => environment().run(request, response))
// it('runs google function environment', () => environment().run(event, context))
Expand Down

0 comments on commit 0982bfd

Please sign in to comment.