Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make serverless/function into a subclass of lambda/function. #202

Merged
merged 14 commits into from
May 2, 2018

Conversation

CyrusNajmabadi
Copy link
Contributor

No description provided.

@CyrusNajmabadi
Copy link
Contributor Author

Note: this is not ready to go in. This is to start the discussion with @lukehoban

let attachment = new RolePolicyAttachment(`${name}-${sha1hash(policy)}`, {
role: this.role,
policyArn: policy,
}, { parent: this });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: parent:this was lost in my translation. the lambda-function needs the role. so it's unclear to me how to accomplish this.

@CyrusNajmabadi
Copy link
Contributor Author

@lukehoban can you take a look?

}
if (!args.role) {
throw new Error("Missing 'role' in 'args'");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This argument validation is taken care of in aws.lambda.Function constructor, soprobably not needed here since we don't use it in this context.

That said - I wonder if we do want to validate that code, handler and any other properties that conflict with these are not passed, since we'll be overwriting those.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that's a good idea. Note: i front loaded the role check because i like early errors. it makes it clear it's the problem of the person who calls us, not a mistake on our end.

serialize = serialize || (_ => true);
const finalSerialize = (o: any) => {
return serialize(o) && o !== this;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate from this change - I'd still love to get rid of this serialize thing alltogether. I believe we think it is not actually needed anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been mitigated... but i think it may still be necessary. effectively, it depends on the asynchrony in user code. If we 'pump' while serializing, it's possible for us to observe loops form, which can then cause deadlocks.

I'll see about removing in a post PR.

const argsCopy = {
...args,
runtime: args.runtime || runtimes.NodeJS8d10Runtime,
timeout: args.timeout === undefined ? 180 : args.timeout,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably remove this line here - and just provide this 180 default via the existing aws.serverless.Function (and possibly in the future some equivalent layer in @pulumi/cloud). Feels like we should only "change" things that we absolutely must to enable closure serialization to be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good.

role: this.role.arn,
timeout: options.timeout === undefined ? 180 : options.timeout,
memorySize: options.memorySize,
deadLetterConfig: options.deadLetterConfig,
vpcConfig: options.vpcConfig,
}, { parent: this });
// createFunction will actually supply the handler. This is just to satisfy typescript.
handler: undefined!,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this mean that any user of createFunction will have to do something awkward like this at it's callsite? That seems unfortunate?

I don't suppose there is any way to create a type that is another type but without a particular set of fields? :-)

Alternatively, we could force handler to be not be marked as required in resources.go. That somewhat weakens type checking for cases where you are calling aws.lambda.Function() directly, but may be worth it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't suppose there is any way to create a type that is another type but without a particular set of fields? :-)

I believe so :) I think typescript added new type-operating functions for precisely thsi purpose. let me look into it.

}, { parent: this });
// createFunction will actually supply the handler. This is just to satisfy typescript.
handler: undefined!,
runtime: options.runtime || lambda.NodeJS6d10Runtime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we overriding this to 6.10 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is hte logic that was already here. I'm not sure our reasoning. @swgillespie ?

[K in O]: (Record<D, never> & Record<string, K>)[K]
}[O]

export type Omit<O, D extends string> = Pick<O, Sub<keyof O, D>>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typescript be crazy!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i had to look at this for a few minutes to wrap my head around what was going on.

@CyrusNajmabadi
Copy link
Contributor Author

@lukehoban can you takea nother look?


export type Sub<O extends string, D extends string> = {
[K in O]: (Record<D, never> & Record<string, K>)[K]
}[O]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the weirdest part for me (specifically the [O] part). I've never used the indexed access operator in creating a type before.

Copy link
Contributor

@lukehoban lukehoban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Longterm, I can imagine perhaps wanting to fold createFunction into the existing new aws.lambda.Function, with a single unified bag of args, but this seems a very pragmatic way to expose creation of a raw callback-based Lambda for now.


// User sholud not supply 'code' or 'handler' to createFunction. We will create those
// out of the handler they pass in.
export type CallbackFunctionArgs = utils.Omit<lambda.FunctionArgs, "code" | "handler">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is public - lets put a doc comment and phrase this as user facing documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

role: this.role.arn,
timeout: options.timeout === undefined ? 180 : options.timeout,
memorySize: options.memorySize,
deadLetterConfig: options.deadLetterConfig,
vpcConfig: options.vpcConfig,
}, { parent: this });
runtime: options.runtime || lambda.NodeJS6d10Runtime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code used to use 8.10, and it seems you are changing it back to use 6.10. Any reason why?

The change to 8.10 was made a couple weeks ago in #194.

}
if ((<any>args).handler) {
throw new Error("'handler' property should not be provided in 'args'");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that these properties are no longer on the type, seems fine to not check for them not being passed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep this in. People may call from JS and they may easily get confused and think they need these due to the docs for lambda.Function. This is a pretty simple check to have, and it could prevent confusion.

// Helpers to create 'subtraction' types. i.e. a type equivalent to some other object type,
// just with some members removed.

export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-| I have no idea how that works, but nice that this is possible.

Are there any downsides to this? I assume error messages are still reasonable when these types are involved?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. the error messages will still be of the form "you need this property 'x'"

export type CallbackFunctionArgs = utils.Omit<lambda.FunctionArgs, "code" | "handler">

/**
* Helper to create an aws lambda function out of an actual javascript callback.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Capitalization of all proper nouns here, also maybe phrase a little more explicitly as a first class part of the API.

@CyrusNajmabadi CyrusNajmabadi merged commit 85ae2b7 into master May 2, 2018
@CyrusNajmabadi CyrusNajmabadi deleted the functionSubclass branch May 2, 2018 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants