- Notifications
You must be signed in to change notification settings - Fork 218
Insert Expression and Field types into GlobalOptions and inheriting types #1171
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
Conversation
spec/v2/providers/tasks.spec.ts Outdated
| () => {} | ||
| ); | ||
| | ||
| expect(result.__trigger).to.deep.equal({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't worry about __trigger annotation for v2.
src/v2/expressions.ts Outdated
| @@ -0,0 +1,54 @@ | |||
| /* | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is param expression specific to v2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code that currently allows users to define params in their functions code lives in the v2 namespace, so...I guess I just assumed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think recent discussion pointed to having params be top level module and not be v2 specific:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go/cf3-params-detailed is the doc that formally proposes ditching v2, but it seems easier for Victor to do that when he reaches a stopping point than having some PRs in v2 and some in root. Besides, technically that should be done in the launch.next branch, right? Technically we're changing an export path.
src/v2/expressions.ts Outdated
| * The CLI does not currently have the capability to evaluate arbitrary CEL expressions. | ||
| * You almost certainly want to use FromParam() instead. | ||
| */ | ||
| export function FromCEL<T extends string | number | boolean>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
q: why is this fn necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1. Remove or @internal this too.
| /** Region of the EventArc trigger. */ | ||
| region?: Field<string>; | ||
| | ||
| /** The service account that EventArc should use to invoke this function. Requires the P4SA to have ActAs permission on this service account. */ | ||
| serviceAccount?: string | null; | ||
| | ||
| /** The name of the channel where the function receives events. */ | ||
| channel?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also not sure where these things are coming from and how it relates to the changes we are making. Especially region and serviceAccount - they are not the same thing as the ones we have defined in GlobalOptions and doesn't seem correct to me.
src/v2/providers/tasks.ts Outdated
| export { AuthData, Request }; | ||
| | ||
| /** How congestion control should be applied to the function. */ | ||
| export interface ParameterizedRateLimits { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we do this instead of wrapping RetryConfig/RateLimits in ../../common/providers/tasks instead?
Is params only supported in v2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in favor of letting v2 functions be configured with Params as well.
spec/v2/providers/tasks.spec.ts Outdated
| () => {} | ||
| ); | ||
| | ||
| expect(result.__trigger).to.deep.equal({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can yank this, we don't use __trigger anymore
src/v2/core.ts Outdated
| minInstances?: number; | ||
| maxInstances?: number; | ||
| availableMemoryMb?: number; | ||
| concurrency?: number | Expression<number>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use TriggerAnnotation anymore, so you can revert these changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're going to hate me for this but:
- Please make an actual Expression class that has things like
.val()and.toString()and.eq(test): BooleanExpressionand aBooleanExpressionclass that implementsthen<T>(trueVal: T, falseVal: T): Expression<T> - In customer-facing SDKs I've been stricter about using typedefs for public types since they can worsen code complete. This is, for example, why I ripped out all typedefs for the handler function signatures in all of our handler code; it simply wasn't helping customers. I think
Field<T>needs to beT | params.Expression<T> | ResetValue
spec/runtime/loader.spec.ts Outdated
| modulePath: './spec/fixtures/sources/commonjs-params', | ||
| expected: { ...expected, params: [{ name: 'FOO', type: 'string' }] }, | ||
| expected: { ...expected, params: [ | ||
| { name: 'FOO', type: 'string' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you, me, and @mbleigh really need to get on the same page for {type: 'select'} vs selectInput: { }.
We cannot release params until we are consistent here.
spec/v2/params.spec.ts Outdated
| | ||
| describe('Param', () => { | ||
| describe('#toSpec()', () => { | ||
| /* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the comments or remove the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can also use it.skip if you intend to enable test at some time in the future.
src/v2/params/types.ts Outdated
| | ||
| /** @hidden */ | ||
| type ParamValueType = 'string' | 'list' | 'boolean' | 'int' | 'float' | 'json'; | ||
| type ParamValueType = 'string' | 'list' | 'boolean' | 'int' | 'float' | 'json' | 'secret'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cut json for now
| const out: ParamSpec = { | ||
| name: this.name, | ||
| ...this.options, | ||
| type: (this.constructor as typeof Param).type, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally wouldn't make SeecretParam inherit from Param because Param is an Expression and SecretParam is not.
src/v2/expressions.ts Outdated
| * resolved to a value of the generic type parameter: i.e, you can pass | ||
| * an Expression<number> as the value of an option that normally accepts numbers. | ||
| */ | ||
| export type Expression<T extends string | number | boolean> = string; // eslint-disable-line |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the Functions codebase an Expression is not the same thing as T. Expression is a base class of Param but can (eventually) do a few extra things:
- BooleanExpressions should subclass Expression and adds the ternary operator to create an Expression
- All (non-boolean?) expressions should support an equality condition to make a BooleanExpression
src/v2/options.ts Outdated
| function isMemoryOption( | ||
| arg: MemoryOption | Expression<number> | ||
| ): arg is MemoryOption { | ||
| return arg in MemoryOptionToMB; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this going to fail for an Expression?
src/v2/providers/tasks.ts Outdated
| export { AuthData, Request }; | ||
| | ||
| /** How congestion control should be applied to the function. */ | ||
| export interface ParameterizedRateLimits { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in favor of letting v2 functions be configured with Params as well.
src/v2/providers/tasks.ts Outdated
| export interface TaskQueueOptions extends options.EventHandlerOptions { | ||
| /** How a task should be retried in the event of a non-2xx return. */ | ||
| retryConfig?: RetryConfig; | ||
| retryConfig?: ParameterizedRetryConfig; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to Daniel's comments. Just modify the normal RetryConfig and RateLimits to support Expressions.
src/v2/expressions.ts Outdated
| export class IfElseExpression< | ||
| T extends string | number | boolean | string[] | ||
| > extends Expression<T> { | ||
| test: Expression<boolean>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this can be cleaned up a good bit with go/tsstyle#parameter-properties
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- other classes
src/v2/expressions.ts Outdated
| export class CompareExpression< | ||
| T extends string | number | boolean | string[] | ||
| > extends Expression<boolean> { | ||
| cmp: '==' | '>' | '>=' | '<' | '<='; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a type to encapsulate this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
¯_(ツ)_/¯ I'm less concerned with that when the only consumer of this API is in the same file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah its definitely a very optional nit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few comments.
spec/v2/params.spec.ts Outdated
| | ||
| describe('Param', () => { | ||
| describe('#toSpec()', () => { | ||
| /* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can also use it.skip if you intend to enable test at some time in the future.
src/runtime/manifest.ts Outdated
| * An definition of a function as appears in the Manifest. | ||
| */ | ||
| export interface ManifestEndpoint { | ||
| export type ManifestEndpoint = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ts question - why was interface => type necessary here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh, it wasn't, I originally did something really dumb with another type that extended ManifestEndpoint (which does require the change), reverted it, and forgot to revert this.
src/v2/expressions.ts Outdated
| @@ -0,0 +1,54 @@ | |||
| /* | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think recent discussion pointed to having params be top level module and not be v2 specific:
src/v2/expressions.ts Outdated
| /* | ||
| * A CEL expression which can be evaulated during function deployment, and | ||
| * resolved to a value of the generic type parameter: i.e, you can pass | ||
| * an Expression<number> as the value of an option that normally accepts numbers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expressions are only used in the context of params. They should be in the same file. You can have a separate file for expressions but v2/params should export everything you need to handle expressions.
src/v2/expressions.ts Outdated
| */ | ||
| export function ExprString<Expression>(arg: Expression): string { | ||
| return (arg as unknown) as string; | ||
| export class ParamExpression< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally tried this, but I found returning an Expression to be a dead end. For me, at least, I had to create new "Base" types for each expression so I could add the valid operators to them. So for example, a StringParam is a StringExpression, which implements eq in addition to being an Expression. The only time I gave up and returned Expression is the then API, but really I should go back and modify that to have N overrides so that each return type is a FooExpression and not an Expression.
src/v2/params/types.ts Outdated
| export type ParamOptions<T = unknown> = Omit<ParamSpec<T>, 'name' | 'type'>; | ||
| | ||
| export class Param<T = unknown> { | ||
| export abstract class Param<T extends string | number | boolean | string[]> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to play with abstract classes a bit to see if it changes my mind about the class hierarchy. I guess I don't mind having all expression types supporting the eq method even though I loose my sanity a smidge when I see "if (bool === true)". But Boolean expressions need to be their own type I think because only boolean expressions can implement then. So maybe.... create a boolean expression type that implements then and make sure eq returns it and that eq is on BooleanParam?
| That latest commit made a fairly significant change to the underlying types of params/expressions to fit the more recent design doc. I'm going to resolve a ton of comments which basically reduce to "change the types/type signatures". |
src/v2/expressions.ts Outdated
| * resolved to a value of the generic type parameter: i.e, you can pass | ||
| * an Expression<number> as the value of an option that normally accepts numbers. | ||
| */ | ||
| export abstract class Expression< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to this, I wonder if things would be cleaner if the two following existed
export type ExpressionOrType<T> = T | Expression<T>; export type NullableExpressionOrType<T> = ExpressionOrType<T> | null; The reason I bring this up is because I saw an earlier instance of boolean | Expression<string> that may not have been intentional. This hardens the typing slightly.
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nits on memory option, but I don't think that should be solved in the PR. this PR has stayed unmerge for too long!
| eventFilters: Record<string, string | Expression<string>>; | ||
| eventFilterPathPatterns?: Record<string, string | Expression<string>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kinda don't get how Expression can be converted to Record<string, string>. Is this something that's handled by the CLI?
| * A value of null restores the defaults of 256MB. | ||
| */ | ||
| memory?: MemoryOption | null; | ||
| memory?: MemoryOption | Expression<number> | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't memoryOptions string?
firebase-functions/src/v2/options.ts
Line 56 in 27a4983
| export type MemoryOption = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see. memory can either be a string or a number.
I think this is a bad footgun. For example, I would write something like this:
{ memory: "{{ isProd ? '1GB' : '256MB' }}" } And this totally wouldn't work no? It would have to be instead:
{ memory: "{{ isProd ? 1024 : 256 }}" } or something which feels surprising.
| * @internal | ||
| */ | ||
| export function clearParams() { | ||
| declaredParams.splice(0, declaredParams.length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this same as declaredParams = []?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be if declaredParams wasn't declared const.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm it's starting to sound like declaredParams isn't a constant variable afterall...
src/v2/params/index.ts Outdated
| */ | ||
| export function defineSecretParam( | ||
| name: string | ||
| //options: ParamOptions<string> = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Stray comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
By changing the types we allow inside the GlobalOptions parameter of the various CF3 setup functions, we automatically plumb them through to the ManifestSpec. Derived from #1168, which was busted enough I decided to just make a new branch.
So far, this PR still does not include:
the trigger type specific parameterized fieldsfull adjustments to the comments for the changed fields
any tests whatsoeverbut it seems like we're going to rely more on integration tests heresanity checking on whether provided strings are actually CEL (but shouldn't we just validate that in the CLI?)
but I wanted to see whether I was totally barking up the wrong tree first.