Skip to content

Commit

Permalink
feat: api gateway with config
Browse files Browse the repository at this point in the history
  • Loading branch information
kamaz committed Nov 13, 2020
1 parent 0499793 commit c8e49c0
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 52 deletions.
27 changes: 27 additions & 0 deletions packages/sls-aws/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions packages/sls-aws/src/api-gateway/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { EnvConfig, Handler, SlsEnvironment, environment } from '@cabiri-io/sls-env'
// eslint-disable-next-line import/no-unresolved
import type { APIGatewayProxyEventV2, APIGatewayProxyResultV2, Context } from 'aws-lambda'

type APIGatewayV2Handler<T = never> = Handler<APIGatewayProxyEventV2, Context, APIGatewayProxyResultV2<T>>

export const apiGatewayV2 = <D, P, R, C = never>(
config?: EnvConfig
): SlsEnvironment<APIGatewayV2Handler<R>, C, D, P, R> => environment<APIGatewayV2Handler<R>, C, D, P, R>(config)
13 changes: 2 additions & 11 deletions packages/sls-aws/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,2 @@
import { EnvConfig, SlsEnvironment, environment } from '@cabiri-io/sls-env'
// eslint-disable-next-line import/no-unresolved
import type { Context, SNSEvent } from 'aws-lambda'
import { jsonSNSMessage } from './sns/json-sns-message'
import { jsonSNSMessages } from './sns/json-sns-messages'

export const snsMessage = <D, P>(config?: EnvConfig): SlsEnvironment<SNSEvent, Context, D, P, void> =>
environment<SNSEvent, Context, D, P, void>(config).payload(jsonSNSMessage)

export const snsMessages = <D, P>(config?: EnvConfig): SlsEnvironment<SNSEvent, Context, D, Array<P>, void> =>
environment<SNSEvent, Context, D, Array<P>, void>(config).payload(jsonSNSMessages)
export { snsMessage, snsMessages } from './sns'
export { apiGatewayV2 } from './api-gateway'
13 changes: 13 additions & 0 deletions packages/sls-aws/src/sns/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { EnvConfig, Handler, SlsEnvironment, environment } from '@cabiri-io/sls-env'
// eslint-disable-next-line import/no-unresolved
import type { Context, SNSEvent } from 'aws-lambda'
import { jsonSNSMessage } from './json-sns-message'
import { jsonSNSMessages } from './json-sns-messages'

type SNSHandler = Handler<SNSEvent, Context, void>

export const snsMessage = <D, P, C = never>(config?: EnvConfig): SlsEnvironment<SNSHandler, C, D, P> =>
environment<SNSHandler, C, D, P>(config).payload(jsonSNSMessage)

export const snsMessages = <D, P, C = never>(config?: EnvConfig): SlsEnvironment<SNSHandler, C, D, Array<P>> =>
environment<SNSHandler, C, D, Array<P>>(config).payload(jsonSNSMessages)
43 changes: 43 additions & 0 deletions packages/sls-env/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 59 additions & 22 deletions packages/sls-env/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable @typescript-eslint/ban-types */
import snakeCase from 'lodash.snakecase'
import { Logger, environment } from '..'
import { Handler, Logger, environment } from '..'

type EmptyEvent = {}
type EmptyContext = {}

describe('serverless environment', () => {
it('supports application', async () =>
environment<EmptyEvent, EmptyContext, void, void, string>()
environment<Handler<EmptyEvent, EmptyContext, string>, never, void, void>()
.app(() => 'hello world!')
.start({}, {})
.then(result => expect(result).toBe('hello world!')))
Expand All @@ -18,7 +18,7 @@ describe('serverless environment', () => {
type EventPayload = { event: MessageEvent; context: NameContext }

it('supports passing an event and context to application', async () =>
environment<MessageEvent, NameContext, void, EventPayload, string>()
environment<Handler<MessageEvent, NameContext, string>, never, void, EventPayload>()
.app(({ payload: { event, context } }) => `${event.message} ${context.name}!`)
.start({ message: 'hello' }, { name: 'world' })
.then(result => expect(result).toBe('hello world!')))
Expand All @@ -32,7 +32,7 @@ describe('serverless environment', () => {

const buildMessage: BuildMessage = (message, name) => `${message} ${name}!`

return environment<MessageEvent, NameContext, BuildMessageDependencies, EventPayload, string>()
return environment<Handler<MessageEvent, NameContext, string>, never, BuildMessageDependencies, EventPayload>()
.global(buildMessage)
.app(({ payload: { event, context }, dependencies: { buildMessage } }) =>
buildMessage(event.message, context.name)
Expand All @@ -47,7 +47,7 @@ describe('serverless environment', () => {
buildMessage: BuildMessage
}

return environment<MessageEvent, NameContext, BuildMessageDependencies, EventPayload, string>()
return environment<Handler<MessageEvent, NameContext, string>, never, BuildMessageDependencies, EventPayload>()
.global({ buildMessage: (message: string, name: string) => `${message} ${name}!` })
.app(({ payload: { event, context }, dependencies: { buildMessage } }) =>
buildMessage(event.message, context.name)
Expand All @@ -62,7 +62,7 @@ describe('serverless environment', () => {
buildMessage: BuildMessage
}

return environment<MessageEvent, NameContext, BuildMessageDependencies, EventPayload, string>()
return environment<Handler<MessageEvent, NameContext, string>, never, BuildMessageDependencies, EventPayload>()
.global('buildMessage', (message: string, name: string) => `${message} ${name}!`)
.app(({ payload: { event, context }, dependencies: { buildMessage } }) =>
buildMessage(event.message, context.name)
Expand All @@ -72,6 +72,11 @@ describe('serverless environment', () => {
})
})

// type A<AA, AB> = (a: AA) => AB
// type C<AC, AD extends A<unknown, unknown>> = (a: AC) => AD

// const a: C<string, A<number, string>> = a => b => a + b

describe('payload', () => {
it('supports mapping payload to custom type', async () => {
type HelloWorldMessage = {
Expand All @@ -87,7 +92,12 @@ describe('serverless environment', () => {
buildMessage: BuildHelloWorldMessage
}

return environment<MessageEvent, NameContext, BuildHelloWorldMessageDependencies, HelloWorldMessage, string>()
return environment<
Handler<MessageEvent, NameContext, string>,
never,
BuildHelloWorldMessageDependencies,
HelloWorldMessage
>()
.global(buildMessage)
.payload((event, context) => ({
hello: event.message,
Expand All @@ -100,7 +110,7 @@ describe('serverless environment', () => {

it('fails when added multiple times', () =>
expect(() =>
environment<EmptyEvent, EmptyContext, void, void, string>()
environment<Handler<EmptyEvent, EmptyContext, string>, never, void, void>()
.app(() => 'hello world!')
.payload(() => {})
.payload(() => {})
Expand All @@ -109,7 +119,7 @@ describe('serverless environment', () => {

describe('logger', () => {
it('has default built in', () =>
environment<EmptyEvent, EmptyContext, void, void, void>()
environment<Handler<EmptyEvent, EmptyContext, void>, never, void, void>()
.app(({ logger }) => expect(logger).toBeDefined())
.start({}, {}))

Expand All @@ -121,7 +131,7 @@ describe('serverless environment', () => {
}
} as unknown) as Logger

return environment<EmptyEvent, EmptyContext, void, void, void>()
return environment<Handler<EmptyEvent, EmptyContext, void>, never, void, void>()
.logger(logger)
.app(({ logger }) => logger.debug('log message'))
.start({}, {})
Expand All @@ -131,14 +141,15 @@ describe('serverless environment', () => {
})
})

// maybe we should have environment variables and all should be consider app configuration?
describe('environment variables mapper', () => {
// how do we define environment variables
it('default to camel case', () => {
process.env.WORKSPACE = 'workspaceValue'
process.env.WORKSPACE_NAME = 'workspaceNameValue'
type EnvDependency = { env: { workspace: string; workspaceName: string } }

return environment<EmptyEvent, EmptyContext, EnvDependency, void, void>()
return environment<Handler<EmptyEvent, EmptyContext, void>, never, EnvDependency, void>()
.app(({ dependencies: { env } }) => {
expect(env.workspace).toBe('workspaceValue')
expect(env.workspaceName).toBe('workspaceNameValue')
Expand All @@ -147,11 +158,12 @@ describe('serverless environment', () => {
})

it('allows to override default variable name', () => {
// fixme: all the env needs to be pushed to configuration and be described as types
process.env.WORKSPACE1 = 'workspace1Value'
process.env.WORKSPACE1_NAME = 'workspaceName1Value'
type EnvDependency = { env: { workspace_1: string; workspace_1_name: string } }

return environment<EmptyEvent, EmptyContext, EnvDependency, void, void>({
return environment<Handler<EmptyEvent, EmptyContext, void>, never, EnvDependency, void>({
envNameMapper: snakeCase
})
.app(({ dependencies: { env } }) => {
Expand All @@ -161,14 +173,39 @@ describe('serverless environment', () => {
.start({}, {})
})
})
// google run against express
// it('runs request / response environment', () => environment().run(request, response))
// it('runs google function environment', () => environment().run(event, context))
/*
const ecbMapping = {
environment: 'ENVIRONMENT',
serviceName: 'SERVICE_NAME',
workspace: 'WORKSPACE',
}
*/

describe('application configuration', () => {
// should app configuration be passed to dependecies
// that could be good as then we can use to to configure all the clients and their timeouts
//
it('creates config from pure function', () => {
type AppConfig = {
message: string
}

return environment<Handler<EmptyEvent, EmptyContext, void>, AppConfig, never, void>()
.config(() => ({ message: 'hello' }))
.app(({ config }) => {
expect(config.message).toBe('hello')
})
.start({}, {})
})

it('creates config from function returning promise', () => {
type AppConfig = {
message: string
}

return environment<Handler<EmptyEvent, EmptyContext, void>, AppConfig, never, void>()
.config(async () => ({ message: 'hello' }))
.app(({ config }) => {
expect(config.message).toBe('hello')
})
.start({}, {})
})

it.todo('create config from object factory')
})

// where and how are we going to add correllaction id
})
Loading

0 comments on commit c8e49c0

Please sign in to comment.