Skip to content

Commit

Permalink
feat: closes #228, closes #230
Browse files Browse the repository at this point in the history
  • Loading branch information
edobrb committed Jan 6, 2024
1 parent 17736ed commit d17c73d
Show file tree
Hide file tree
Showing 40 changed files with 417 additions and 264 deletions.
4 changes: 2 additions & 2 deletions packages/aws-lambda-rest/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import { getAbsoluteFSPath } from 'swagger-ui-dist'

export type Context = { lambdaApi: { request: Request; response: Response } }

export function build<const Fs extends functions.Functions, const ContextInput>({
export function build<Fs extends functions.Functions, E extends functions.ErrorType, ContextInput>({
api,
context,
error,
customize,
...args
}: {
api: rest.Api<Fs, ContextInput>
api: rest.Api<Fs, E, ContextInput>
context: (serverContext: Context) => Promise<ContextInput>
error?: rest.ErrorHandler<Fs, Context>
options: Partial<rest.ServeOptions>
Expand Down
8 changes: 4 additions & 4 deletions packages/aws-lambda-rest/src/methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { rest, utils } from '@mondrian-framework/rest'
import { http, isArray } from '@mondrian-framework/utils'
import { API, HandlerFunction, METHODS } from 'lambda-api'

export function attachRestMethods<const Fs extends functions.Functions, const ContextInput>({
export function attachRestMethods<Fs extends functions.Functions, E extends functions.ErrorType, ContextInput>({
server,
api,
context,
error,
}: {
server: API
api: rest.Api<Fs, ContextInput>
api: rest.Api<Fs, E, ContextInput>
context: (serverContext: Context) => Promise<ContextInput>
error?: rest.ErrorHandler<Fs, Context>
}): void {
Expand All @@ -29,14 +29,14 @@ export function attachRestMethods<const Fs extends functions.Functions, const Co
maxVersion: api.version,
})
.map((p) => p.replace(/{(.*?)}/g, ':$1'))
const restHandler = rest.handler.fromFunction<Fs, Context, ContextInput>({
const restHandler = rest.handler.fromFunction<Fs, E, Context, ContextInput>({
module: api.module,
context,
specification,
functionName,
functionBody,
error,
api,
api: api as any,
})
const lambdaApiHandler: HandlerFunction = async (request, response) => {
const result = await restHandler({
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-lambda-sqs/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ type FunctionSpecifications = {
| { anyQueue: true }
)

export function build<const Fs extends functions.Functions, CI>({
export function build<Fs extends functions.Functions, E extends functions.ErrorType, CI>({
module,
api,
context,
}: {
module: module.Module<Fs, CI>
module: module.Module<Fs, E, CI>
api: Api<Fs>
context: (args: { event: SQSEvent; context: Context; recordIndex: number }) => Promise<CI>
}): SQSHandler {
Expand Down
8 changes: 5 additions & 3 deletions packages/aws-sqs/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export type ApiSpecification<Fs extends functions.FunctionsInterfaces> = {
* attach SQS to any functions and consuming it's messages.
* In order to instantiate this you should use {@link build}.
*/
export type Api<Fs extends functions.Functions, CI> = ApiSpecification<Fs> & {
module: module.Module<Fs, CI>
export type Api<Fs extends functions.Functions, E extends functions.ErrorType, CI> = ApiSpecification<Fs> & {
module: module.Module<Fs, E, CI>
}

export type FunctionSpecifications = {
Expand All @@ -35,7 +35,9 @@ export type FunctionSpecifications = {
/**
* Builds a SQS API in order to attach the module to the queues.
*/
export function build<const Fs extends functions.Functions, CI>(api: Api<Fs, CI>): Api<Fs, CI> {
export function build<Fs extends functions.Functions, E extends functions.ErrorType, CI>(
api: Api<Fs, E, CI>,
): Api<Fs, E, CI> {
//TODO [Good first issue]: check validity of api as rest.build
return api
}
Expand Down
8 changes: 4 additions & 4 deletions packages/aws-sqs/src/listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { sleep } from '@mondrian-framework/utils'
/**
* TODO: doc
*/
export function listen<const Fs extends functions.Functions, const CI>({
export function listen<Fs extends functions.Functions, E extends functions.ErrorType, CI>({
api,
context,
}: {
api: Api<Fs, CI>
api: Api<Fs, E, CI>
context: (args: { message: AWS.Message }) => Promise<CI>
}): { close: () => Promise<void> } {
const client: AWS.SQS = new AWS.SQS(api.options?.config ?? {})
Expand Down Expand Up @@ -48,7 +48,7 @@ export function listen<const Fs extends functions.Functions, const CI>({
}
}

async function listenForMessage<const Fs extends functions.Functions, const CI>({
async function listenForMessage<Fs extends functions.Functions, E extends functions.ErrorType, CI>({
alive,
queueUrl,
client,
Expand All @@ -61,7 +61,7 @@ async function listenForMessage<const Fs extends functions.Functions, const CI>(
queueUrl: string
alive: { yes: boolean }
client: AWS.SQS
module: module.Module<Fs, CI>
module: module.Module<Fs, E, CI>
functionName: string
context: (args: { message: AWS.Message }) => Promise<CI>
specifications: FunctionSpecifications
Expand Down
10 changes: 6 additions & 4 deletions packages/ci-tools/src/impl/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import { moduleInterface } from '../interface'
import { buildGraphQLReport } from './build-graphql-report'
import { buildOASReport } from './build-oas-report'
import { getReport } from './get-report'
import { result } from '@mondrian-framework/model'

export type Context = {
readonly fileManager: FileManager
readonly serverBaseURL: string
}

export const module = moduleInterface.implement({
context: async () => ({
fileManager: process.env.BUCKET ? S3_FILE_MANAGER : LOCAL_FILE_MANAGER,
serverBaseURL: process.env.SERVER_BASE_URL ?? 'http://localhost:4010',
}),
context: async () =>
result.ok({
fileManager: process.env.BUCKET ? S3_FILE_MANAGER : LOCAL_FILE_MANAGER,
serverBaseURL: process.env.SERVER_BASE_URL ?? 'http://localhost:4010',
}),
functions: { getReport, buildOASReport, buildGraphQLReport },
options: { checkOutputType: 'throw' },
})
8 changes: 5 additions & 3 deletions packages/cron/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export type ApiSpecification<Fs extends functions.FunctionsInterfaces> = {
* schedule functions execution with a cron string.
* In order to instantiate this you should use {@link build}.
*/
export type Api<Fs extends functions.Functions, CI> = ApiSpecification<Fs> & {
module: module.Module<Fs, CI>
export type Api<Fs extends functions.Functions, E extends functions.ErrorType, CI> = ApiSpecification<Fs> & {
module: module.Module<Fs, E, CI>
}

export type FunctionSpecifications<InputType extends model.Type> = {
Expand All @@ -37,7 +37,9 @@ type InputGenerator<InputType extends model.Type>
/**
* Builds a cron API in order to schedule function execution.
*/
export function build<const Fs extends functions.Functions, CI>(api: Api<Fs, CI>): Api<Fs, CI> {
export function build<Fs extends functions.Functions, E extends functions.ErrorType, CI>(
api: Api<Fs, E, CI>,
): Api<Fs, E, CI> {
//TODO [Good first issue]: check validity of api as rest.build
return api
}
Expand Down
4 changes: 2 additions & 2 deletions packages/cron/src/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import { schedule, validate } from 'node-cron'
* Starts a new cron listeners with the given configuration.
* For each cron assigned function a new schedule is created.
*/
export function start<const F extends functions.Functions, CI>({
export function start<F extends functions.Functions, E extends functions.ErrorType, CI>({
api,
context,
}: {
api: Api<F, CI>
api: Api<F, E, CI>
context: (args: { cron: string }) => Promise<CI>
}): { close: () => Promise<void> } {
const baseLogger = logger.build({ moduleName: api.module.name, server: 'CRON' })
Expand Down
12 changes: 8 additions & 4 deletions packages/direct/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,22 @@ export type ApiSpecification<
*/
export type Api<
Fs extends functions.Functions,
E extends functions.ErrorType,
Exclusions extends { [K in keyof Fs]?: true },
ContextInput,
> = ApiSpecification<Fs, Exclusions> & {
module: module.Module<Fs, ContextInput>
module: module.Module<Fs, E, ContextInput>
}

/**
* Builds a Direct API in order to expose the module.
*/
export function build<Fs extends functions.Functions, Exclusions extends { [K in keyof Fs]?: true }, ContextInput>(
api: Api<Fs, Exclusions, ContextInput>,
): Api<Fs, Exclusions, ContextInput> {
export function build<
Fs extends functions.Functions,
E extends functions.ErrorType,
Exclusions extends { [K in keyof Fs]?: true },
ContextInput,
>(api: Api<Fs, E, Exclusions, ContextInput>): Api<Fs, E, Exclusions, ContextInput> {
//assertApiValidity(api) //TODO [Good first issue]: as rest.assertApiValidity
return api
}
Expand Down
31 changes: 20 additions & 11 deletions packages/direct/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ export type Response = SuccessResponse | FailureResponse
/**
* Gets an http handler with the implementation of the Direct transport for a whole Mondrian module.
*/
export function fromModule<Fs extends functions.Functions, ServerContext, ContextInput>({
export function fromModule<Fs extends functions.Functions, E extends functions.ErrorType, ServerContext, ContextInput>({
api,
context,
options,
}: {
api: Api<Fs, any, ContextInput>
api: Api<Fs, E, any, ContextInput>
context: (serverContext: ServerContext, metadata: Record<string, string> | undefined) => Promise<ContextInput>
options: ServeOptions
}): http.Handler<ServerContext, unknown, Response> {
Expand Down Expand Up @@ -103,7 +103,12 @@ export function fromModule<Fs extends functions.Functions, ServerContext, Contex
return handler
}

async function handleFunctionCall<Fs extends functions.Functions, ServerContext, ContextInput>({
async function handleFunctionCall<
Fs extends functions.Functions,
E extends functions.ErrorType,
ServerContext,
ContextInput,
>({
functionName,
tracer,
requestInputTypeMap,
Expand All @@ -118,7 +123,7 @@ async function handleFunctionCall<Fs extends functions.Functions, ServerContext,
requestInputTypeMap: Record<string, model.ConcreteType>
request: http.Request
options: ServeOptions
api: Api<Fs, any, ContextInput>
api: Api<Fs, E, any, ContextInput>
serverContext: ServerContext
context: (serverContext: ServerContext, metadata: Record<string, string> | undefined) => Promise<ContextInput>
}): Promise<result.Result<SuccessResponse, FailureResponse>> {
Expand Down Expand Up @@ -146,26 +151,30 @@ async function handleFunctionCall<Fs extends functions.Functions, ServerContext,

try {
const contextInput = await context(serverContext, metadata)
const contextValue = await api.module.context(contextInput, {
const ctxResult = await api.module.context(contextInput, {
functionName,
input,
tracer: functionBody.tracer,
retrieve: thisRetrieve,
logger: baseLogger,
})
const functionReturn = await functionBody.apply({
context: contextValue,
if (ctxResult.isFailure) {
const response = successResponse.encodeWithoutValidation({
success: true,
failure: ctxResult.error as never,
}) as SuccessResponse
return result.ok(response)
}
const applyResult = await functionBody.apply({
context: ctxResult.value,
input,
tracer: functionBody.tracer,
retrieve: thisRetrieve,
logger: baseLogger,
})
const functionResult: result.Result<unknown, unknown> = functionBody.errors
? functionReturn
: result.ok(functionReturn)
const response = successResponse.encodeWithoutValidation({
success: true,
...(functionResult.isOk ? { result: functionResult.value as never } : { failure: functionResult.error as never }),
...(applyResult.isOk ? { result: applyResult.value as never } : { failure: applyResult.error as never }),
}) as SuccessResponse
return result.ok(response)
} catch (error) {
Expand Down
30 changes: 20 additions & 10 deletions packages/direct/src/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import { ApiSpecification } from './api'
import { Response } from './handler'
import { result, model } from '@mondrian-framework/model'
import { functions, sdk, retrieve } from '@mondrian-framework/module'
import { functions, sdk, retrieve, utils } from '@mondrian-framework/module'
import { flatMapObject, http } from '@mondrian-framework/utils'

export type Sdk<Fs extends functions.FunctionsInterfaces, Exclusions extends { [K in keyof Fs]?: true }> = {
functions: SdkFunctions<Omit<Fs, keyof Exclusions & keyof Fs>>
withMetadata: (metadata: Record<string, string>) => Sdk<Fs, Exclusions>
export type Sdk<
Fs extends functions.FunctionsInterfaces,
E extends functions.ErrorType,
Exclusions extends { [K in keyof Fs]?: true },
> = {
functions: SdkFunctions<Omit<Fs, keyof Exclusions & keyof Fs>, E>
withMetadata: (metadata: Record<string, string>) => Sdk<Fs, E, Exclusions>
}

type SdkFunctions<Fs extends functions.FunctionsInterfaces> = {
[K in keyof Fs]: SdkFunction<Fs[K]['input'], Fs[K]['output'], Fs[K]['errors'], Fs[K]['retrieve']>
type SdkFunctions<Fs extends functions.FunctionsInterfaces, E extends functions.ErrorType> = {
[K in keyof Fs]: SdkFunction<
Fs[K]['input'],
Fs[K]['output'],
utils.MergeErrors<Fs[K]['errors'], E>,
Fs[K]['retrieve']
>
}

type SdkFunction<
Expand Down Expand Up @@ -45,8 +54,9 @@ type SdkFunctionResult<
* Builds a new client that will connect to a Mondrian Direct endpoint.
*/
export function build<
const Fs extends functions.FunctionsInterfaces,
const Exclusions extends { [K in keyof Fs]?: true },
Fs extends functions.FunctionsInterfaces,
E extends functions.ErrorType,
Exclusions extends { [K in keyof Fs]?: true },
>({
endpoint,
api,
Expand All @@ -57,7 +67,7 @@ export function build<
api: ApiSpecification<Fs, Exclusions>
metadata?: Record<string, string>
fetchOptions?: Omit<RequestInit, 'method' | 'headers' | 'body'> & { headers?: Record<string, string | undefined> }
}): Sdk<Fs, Exclusions> {
}): Sdk<Fs, E, Exclusions> {
const funcs = flatMapObject(api.module.functions, (functionName, functionBody) => {
if (api.exclusions[functionName]) {
return []
Expand Down Expand Up @@ -153,7 +163,7 @@ export function build<
})

return {
functions: funcs as unknown as Sdk<Fs, Exclusions>['functions'],
functions: funcs as unknown as Sdk<Fs, E, Exclusions>['functions'],
withMetadata: (metadata) => build({ endpoint, api, metadata }),
}
}
6 changes: 3 additions & 3 deletions packages/direct/src/server/fastify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ export type ServerContext = { request: FastifyRequest; reply: FastifyReply }
/**
* Attachs a Direct server to a fastify instace.
*/
export function serveWithFastify<const Fs extends functions.Functions, const ContextInput>({
export function serveWithFastify<Fs extends functions.Functions, E extends functions.ErrorType, ContextInput>({
server,
api,
context,
...args
}: {
api: Api<Fs, any, ContextInput>
api: Api<Fs, E, any, ContextInput>
server: FastifyInstance
context: (serverContext: ServerContext, metadata: Record<string, string> | undefined) => Promise<ContextInput>
options?: Partial<ServeOptions>
}): void {
const options = { ...DEFAULT_SERVE_OPTIONS, ...args.options }
const handler = fromModule<Fs, ServerContext, ContextInput>({ api, context, options })
const handler = fromModule<Fs, E, ServerContext, ContextInput>({ api, context, options })
const path = api.options?.path ?? '/mondrian'
server.post(path, async (request, reply) => {
const response = await handler({
Expand Down

0 comments on commit d17c73d

Please sign in to comment.