Skip to content

Commit

Permalink
Merge pull request #1093 from m-radzikowski/internal-typing
Browse files Browse the repository at this point in the history
feat(typescript): getInternal type safety + other type improvements
  • Loading branch information
willfarrell authored Oct 8, 2023
2 parents 43ec341 + a673016 commit 776b2f0
Show file tree
Hide file tree
Showing 40 changed files with 1,458 additions and 254 deletions.
40 changes: 28 additions & 12 deletions packages/appconfig/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,38 @@ import {
StartConfigurationSessionRequest
} from '@aws-sdk/client-appconfigdata'

export type Options<AwsAppConfigClient = AppConfigDataClient>
= Omit<MiddyOptions<AwsAppConfigClient, AppConfigDataClientConfig>, 'fetchData'>
& {
fetchData?: {
[configurationRequestKey: string]: StartConfigurationSessionRequest
}
}
export type ParamType<T> = StartConfigurationSessionRequest & { __returnType?: T }
export declare function appConfigReq<T> (req: StartConfigurationSessionRequest): ParamType<T>

export type Context<TOptions extends Options | undefined> = TOptions extends {
setToContext: true
export interface AppConfigOptions<AwsAppConfigClient = AppConfigDataClient>
extends Omit<MiddyOptions<AwsAppConfigClient, AppConfigDataClientConfig>, 'fetchData'> {
fetchData?: { [key: string]: StartConfigurationSessionRequest | ParamType<unknown> }
}
? LambdaContext & Record<keyof TOptions['fetchData'], any>

export type Context<TOptions extends AppConfigOptions | undefined> =
TOptions extends { setToContext: true }
? TOptions extends { fetchData: infer TFetchData }
? LambdaContext & {
[Key in keyof TFetchData]: TFetchData[Key] extends ParamType<infer T>
? T
: unknown
}
: never
: LambdaContext

declare function appConfigMiddleware<TOptions extends Options | undefined> (
export type Internal<TOptions extends AppConfigOptions | undefined> =
TOptions extends AppConfigOptions
? TOptions extends { fetchData: infer TFetchData }
? {
[Key in keyof TFetchData]: TFetchData[Key] extends ParamType<infer T>
? T
: unknown
}
: {}
: {}

declare function appConfigMiddleware<TOptions extends AppConfigOptions> (
options?: TOptions
): middy.MiddlewareObj<unknown, any, Error, Context<TOptions>>
): middy.MiddlewareObj<unknown, any, Error, Context<TOptions>, Internal<TOptions>>

export default appConfigMiddleware
5 changes: 5 additions & 0 deletions packages/appconfig/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,9 @@ const appConfigMiddleware = (opts = {}) => {
}
export default appConfigMiddleware

// used for TS type inference (see index.d.ts)
export function appConfigReq (req) {
return req
}

// # sourceMappingURL=index.js.map
81 changes: 70 additions & 11 deletions packages/appconfig/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { AppConfigDataClient } from '@aws-sdk/client-appconfigdata'
import { Context as LambdaContext } from 'aws-lambda'
import { captureAWSv3Client } from 'aws-xray-sdk'
import { expectType } from 'tsd'
import appConfig from '.'
import appConfig, { Context, appConfigReq } from '.'
import { getInternal } from '@middy/util'

const options = {
AwsClient: AppConfigDataClient,
Expand All @@ -16,13 +17,6 @@ const options = {
},
awsClientAssumeRole: 'some-role',
awsClientCapture: captureAWSv3Client,
fetchData: {
config: {
ApplicationIdentifier: 'app',
ConfigurationProfileIdentifier: 'configId',
EnvironmentIdentifier: 'development'
}
},
disablePrefetch: true,
cacheKey: 'some-key',
cacheExpiry: 60 * 60 * 5,
Expand All @@ -35,13 +29,38 @@ expectType<middy.MiddlewareObj<unknown, any, Error, LambdaContext>>(
)

// use with all options
expectType<middy.MiddlewareObj<unknown, any, Error, LambdaContext>>(
expectType<middy.MiddlewareObj<unknown, any, Error, Context<typeof options>>>(
appConfig(options)
)

// use with setToContext: false
expectType<middy.MiddlewareObj<unknown, any, Error, LambdaContext, Record<'config', unknown>>>(
appConfig({
...options,
fetchData: {
config: {
ApplicationIdentifier: 'app',
ConfigurationProfileIdentifier: 'configId',
EnvironmentIdentifier: 'development'
}
},
setToContext: false
})
)

// use with setToContext: true
expectType<middy.MiddlewareObj<unknown, any, Error, LambdaContext & Record<'config', any>>>(
appConfig({ ...options, setToContext: true })
expectType<middy.MiddlewareObj<unknown, any, Error, LambdaContext & Record<'config', unknown>, Record<'config', unknown>>>(
appConfig({
...options,
fetchData: {
config: {
ApplicationIdentifier: 'app',
ConfigurationProfileIdentifier: 'configId',
EnvironmentIdentifier: 'development'
}
},
setToContext: true
})
)

// @ts-expect-error - fetchData must be an object
Expand All @@ -68,3 +87,43 @@ appConfig({
config: {}
}
})

const handler = middy(async (event: {}, context: LambdaContext) => {
return await Promise.resolve({})
})

handler.use(
appConfig({
fetchData: {
config: appConfigReq<{ config1: string, config2: string, config3: number }>({
ApplicationIdentifier: 'app',
ConfigurationProfileIdentifier: 'configId',
EnvironmentIdentifier: 'development'
})
},
setToContext: true
})
)
.before(async (request) => {
expectType<{ config1: string, config2: string, config3: number }>(request.context.config)

const data = await getInternal('config', request)
expectType<string>(data.config.config1)
})

handler.use(
appConfig({
fetchData: {
config: appConfigReq<{ config1: string, config2: string, config3: number }>({
ApplicationIdentifier: 'app',
ConfigurationProfileIdentifier: 'configId',
EnvironmentIdentifier: 'development'
})
},
setToContext: false
})
)
.before(async (request) => {
const data = await getInternal('config', request)
expectType<{ config1: string, config2: string, config3: number }>(data.config)
})
29 changes: 0 additions & 29 deletions packages/appconfig/package-lock.json

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

68 changes: 39 additions & 29 deletions packages/core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,34 @@ export interface Request<
TEvent = any,
TResult = any,
TErr = Error,
TContext extends LambdaContext = LambdaContext
TContext extends LambdaContext = LambdaContext,
TInternal extends Record<string, unknown> = {}
> {
event: TEvent
context: TContext
response: TResult | null
error: TErr | null
internal: {
[key: string]: any
}
internal: TInternal
}

declare type MiddlewareFn<
TEvent = any,
TResult = any,
TErr = Error,
TContext extends LambdaContext = LambdaContext
> = (request: Request<TEvent, TResult, TErr, TContext>) => any
TContext extends LambdaContext = LambdaContext,
TInternal extends Record<string, unknown> = {}
> = (request: Request<TEvent, TResult, TErr, TContext, TInternal>) => any

export interface MiddlewareObj<
TEvent = unknown,
TResult = any,
TErr = Error,
TContext extends LambdaContext = LambdaContext
TContext extends LambdaContext = LambdaContext,
TInternal extends Record<string, unknown> = {}
> {
before?: MiddlewareFn<TEvent, TResult, TErr, TContext>
after?: MiddlewareFn<TEvent, TResult, TErr, TContext>
onError?: MiddlewareFn<TEvent, TResult, TErr, TContext>
before?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
after?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
onError?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
}

// The AWS provided Handler type uses void | Promise<TResult> so we have no choice but to follow and suppress the linter warning
Expand All @@ -79,57 +80,63 @@ export interface MiddyfiedHandler<
TEvent = any,
TResult = any,
TErr = Error,
TContext extends LambdaContext = LambdaContext
TContext extends LambdaContext = LambdaContext,
TInternal extends Record<string, unknown> = {}
> extends MiddyInputHandler<TEvent, TResult, TContext>,
MiddyInputPromiseHandler<TEvent, TResult, TContext> {
use: UseFn<TEvent, TResult, TErr, TContext>
before: AttachMiddlewareFn<TEvent, TResult, TErr, TContext>
after: AttachMiddlewareFn<TEvent, TResult, TErr, TContext>
onError: AttachMiddlewareFn<TEvent, TResult, TErr, TContext>
use: UseFn<TEvent, TResult, TErr, TContext, TInternal>
before: AttachMiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
after: AttachMiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
onError: AttachMiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
handler: <TAdditional>(
handler: MiddlewareHandler<
LambdaHandler<TEvent & TAdditional, TResult>,
TContext
>
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext>
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>
}

declare type AttachMiddlewareFn<
TEvent = any,
TResult = any,
TErr = Error,
TContext extends LambdaContext = LambdaContext
TContext extends LambdaContext = LambdaContext,
TInternal extends Record<string, unknown> = {}
> = (
middleware: MiddlewareFn<TEvent, TResult, TErr, TContext>
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext>
middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>

declare type AttachMiddlewareObj<
TEvent = any,
TResult = any,
TErr = Error,
TContext extends LambdaContext = LambdaContext
TContext extends LambdaContext = LambdaContext,
TInternal extends Record<string, unknown> = {}
> = (
middleware: MiddlewareObj<TEvent, TResult, TErr, TContext>
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext>
middleware: MiddlewareObj<TEvent, TResult, TErr, TContext, TInternal>
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>

declare type UseFn<
TEvent = any,
TResult = any,
TErr = Error,
TContext extends LambdaContext = LambdaContext
> = <TMiddleware extends MiddlewareObj<any, any, Error, any>>(
TContext extends LambdaContext = LambdaContext,
TInternal extends Record<string, unknown> = {}
> = <TMiddleware extends MiddlewareObj<any, any, Error, any, any>>(
middlewares: TMiddleware | TMiddleware[]
) => TMiddleware extends MiddlewareObj<
infer TMiddlewareEvent,
any,
Error,
infer TMiddlewareContext
infer TMiddlewareContext,
infer TMiddlewareInternal
>
? MiddyfiedHandler<
TMiddlewareEvent & TEvent,
TResult,
TErr,
TMiddlewareContext & TContext
TMiddlewareContext & TContext,
TMiddlewareInternal & TInternal
> // always true
: never

Expand All @@ -149,11 +156,14 @@ declare function middy<
TEvent = unknown,
TResult = any,
TErr = Error,
TContext extends LambdaContext = LambdaContext
TContext extends LambdaContext = LambdaContext,
TInternal extends Record<string, unknown> = {}
> (
handler?: MiddlewareHandler<LambdaHandler<TEvent, TResult>, TContext> | PluginObject,
handler?:
| MiddlewareHandler<LambdaHandler<TEvent, TResult>, TContext>
| PluginObject,
plugin?: PluginObject
): MiddyfiedHandler<TEvent, TResult, TErr, TContext>
): MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>

declare namespace middy {
export {
Expand Down
Loading

0 comments on commit 776b2f0

Please sign in to comment.