serverless-offline-streams
Version:
This plugin provides support for event driven systems using Serverless Offline
104 lines (82 loc) • 3.71 kB
text/typescript
import {FilterPatterns} from "./dynamodb/filterPatterns/filterGrammar";
import * as Serverless from "serverless";
import {StringKeyObject} from "./utils";
export interface FunctionDefinition<EventType> {
handler: string
events: Array<EventType>
}
type ServerlessFunctionDefinition = FunctionDefinition<StringKeyObject<any>>
export interface ParsedFunctionDefinition extends FunctionDefinition<StreamsEventMapping> {
functionName: string
}
export interface SqsEventMappingDefinition {
sqs: {
arn: string
batchSize?: number
}
}
export interface DynamoEventMappingDefinition {
stream: {
type: "dynamodb"
arn: string
enabled?: boolean
batchSize?: number
startingPosition?: 'LATEST' | 'TRIM_HORIZON'
filterPatterns: FilterPatterns[]
}
}
type StreamsEventMappingDefinition = SqsEventMappingDefinition | DynamoEventMappingDefinition
type StreamsFunctionType = 'DYNAMO' | 'SQS'
export interface StreamsEventMapping {
type: StreamsFunctionType
sourceEvent: StreamsEventMappingDefinition
}
export const getFunctionDefinitionsWithStreamsEvents = (serverless: Serverless, type?: StreamsFunctionType): StringKeyObject<ParsedFunctionDefinition> => {
const {service} = serverless
const rawFunctionsConfig = (serverless as unknown as any).configurationInput.functions as StringKeyObject<any>
const zipWithIndex = <T>(v: T, i: number) => [v, i] as [T, number]
const parseFilterPatterns = (functionName: string, eventIndex: number) => {
return rawFunctionsConfig[functionName]?.events[eventIndex]?.stream?.filterPatterns
};
const isValidStreamsEvent = (event: any) => (!type && event.sqs || event.stream) ||
(type === 'DYNAMO' && event?.stream?.type === 'dynamodb') ||
(type === 'SQS' && event?.sqs)
const toSqsStreamsEventMapping = (event: StringKeyObject<any>): StreamsEventMapping => ({
type: 'SQS',
sourceEvent: event as SqsEventMappingDefinition
})
const toDynamoStreamsEventMapping = (functionName: string, event: StringKeyObject<any>, eventIndex: number): StreamsEventMapping => ({
type: 'DYNAMO',
sourceEvent: {
...event,
stream: {
...event.stream,
filterPatterns: event.filterPatterns ? parseFilterPatterns(functionName, eventIndex) : undefined
}
}
})
const toStreamsEventMapping = (functionName: string, event: StringKeyObject<any>, eventIndex: number): StreamsEventMapping => {
if (event.sqs) return toSqsStreamsEventMapping(event)
if (event.stream) return toDynamoStreamsEventMapping(functionName, event, eventIndex)
throw Error("Unable to parse streams event mapping")
}
const parseFunctionWithStreamsEvents = (functionName: string): ParsedFunctionDefinition => {
const functionDef = service.getFunction(functionName) as ServerlessFunctionDefinition
return {
functionName,
...functionDef,
events: functionDef.events
.map(zipWithIndex)
.filter(([e]) => isValidStreamsEvent(e))
.map(([e, i]) => toStreamsEventMapping(functionName, e, i)),
}
}
const hasStreamsEvents = (functionDef: ParsedFunctionDefinition) => functionDef.events.length > 0
const zipWithName = (functionDef: ParsedFunctionDefinition) => [functionDef.functionName, functionDef] as [string, ParsedFunctionDefinition]
return Object.fromEntries(
service.getAllFunctions()
.map(parseFunctionWithStreamsEvents)
.filter(hasStreamsEvents)
.map(zipWithName)
)
}