agiledigital-labs / aws-durable-lambda

3 stars 1 forks source link

Consider inverting the relationship between the plugin machinery and user functions #107

Open dspasojevic opened 2 years ago

dspasojevic commented 2 years ago

Right now, the plugin creates one set of http and sqs functions that will call any function from the user's application.

We should consider kungfu-ing this around so that:

  1. we create a new event type (e.g. durablelambda)
  2. the user annotates the functions that they want to expose with that event type
  3. the plugin processes those events to create new handler functions
  4. the sqs events remain as is

So the user would do something like:

const exampleFunction = {
  handler: `${handlerPath(__dirname)}/example.defaultHandler`,
  events: [
    {
      durableLambda: {
        path: '/things/{id}/clone-the-thing',
        authorizer: {
          name: 'cognitoAuthorizer',
        },
      },
    },
  ]
}

And then the plugin would create an instance of the submit and get handlers, wired to the expected http events.

@NoxHarmonium @haolinj what do you think about this? If it is a good idea, we should do it sooner rather than later.

dspasojevic commented 2 years ago

Doing this would make addressing:

          // Currently the target function ARN is provided by an API call
          // and is not known ahead of time so this statement is permissive.
          // In the future we might allow the consumer of the library to
          // narrow this down.

a bit easier as well.

NoxHarmonium commented 2 years ago

I think this is how I would expect it to work if I was installing aws-durable-lambda with no prior knowledge of how it worked (although I might be biased by all my work on https://github.com/agiledigital/serverless-sns-sqs-lambda)

I think it is a good way to go, especially since, as you mention, it would give us a list of functions that we need permission to call and we could lock down the permissions.

I'm going to have a think about the easiest way to delegate HTTP event attributes like the authorizer attribute that you have in the example above to the generated HTTP handlers. I guess we could either just add attributes we need as we discover them and map then through. Otherwise maybe we could just have a property where you can provide a map of attributes that are directly passed through to the HTTP handlers.

E.g.

const exampleFunction = {
  handler: `${handlerPath(__dirname)}/example.defaultHandler`,
  events: [
    {
      durableLambda: {
        path: '/things/{id}/clone-the-thing',
        httpEventAttributes: {
          authorizer: {
            name: 'cognitoAuthorizer',
          },
        },
      },
    },
  ]
}

Does that make sense?

dspasojevic commented 2 years ago

Yep, that makes sense. I reckon an explicit httpEventAttributes is good, especially since it makes it more obvious how those settings will be used.

I suppose it will be a subset of the attributes of http events, since some attributes won't make sense for us (e.g. method)?

NoxHarmonium commented 2 years ago

Yeah that makes sense, we could whitelist a set of attributes that make sense in our context.

NoxHarmonium commented 1 year ago

I just realised one potential complication: some projects use API Gateway v1 (http events) and some projects use API Gateway v2 (httpApi events). We might need to cater for both options (unless it is easy to mix and match both versions and we just pick one and stick with it)

NoxHarmonium commented 1 year ago

I haven't got very far with this, but this is the config schema I wrote that would pass through APIG v1 attributes

    serverless.configSchemaHandler.defineFunctionEvent("aws", "durableLambda", {
      type: "object",
      properties: {
        path: { type: 'string', regexp: /^(?:\*|\/?\S*)$/.toString() },
        // Serverless http/httpApi event definitions
        // http Event: https://github.com/serverless/serverless/blob/4169ae183f64c5c580d90e653e23cc3c52a6f971/lib/plugins/aws/package/compile/events/api-gateway/index.js#L186
        // httpApi Event: https://github.com/serverless/serverless/blob/4169ae183f64c5c580d90e653e23cc3c52a6f971/lib/plugins/aws/package/compile/events/http-api.js#L73
        httpEventAttributes: {
          type: 'object',
          properties: {
            async: { type: 'boolean' },
            authorizer: authorizerSchema,
            connectionId: { $ref: '#/definitions/awsCfInstruction' },
            connectionType: { anyOf: ['vpc-link', 'VPC_LINK'].map(caseInsensitive) },
            cors: corsSchema,
            integration: {
              anyOf: [
                'LAMBDA_PROXY',
                'LAMBDA-PROXY',
                'LAMBDA',
                'AWS',
                'AWS_PROXY',
                'AWS-PROXY',
                'HTTP',
                'HTTP_PROXY',
                'HTTP-PROXY',
                'MOCK',
              ].map(caseInsensitive),
            },
            operationId: { type: 'string' },
            private: { type: 'boolean' },
          },
          required: ['path', 'method'],
          additionalProperties: false,
        },
    });