@@ -57,16 +57,26 @@ export abstract class Expression<T extends string | number | boolean | string[]>
57
57
}
58
58
}
59
59
60
- function quoteIfString < T extends string | number | boolean | string [ ] > ( literal : T ) : T {
61
- // TODO(vsfan@): CEL's string escape semantics are slightly different than Javascript's, what do we do here?
62
- return typeof literal === "string" ? ( `"${ literal } "` as T ) : literal ;
63
- }
64
-
65
60
function valueOf < T extends string | number | boolean | string [ ] > ( arg : T | Expression < T > ) : T {
66
61
return arg instanceof Expression ? arg . runtimeValue ( ) : arg ;
67
62
}
63
+ /**
64
+ * Returns how an entity (either an Expression or a literal value) should be represented in CEL.
65
+ * - Expressions delegate to the .toString() method, which is used by the WireManifest
66
+ * - Strings have to be quoted explicitly
67
+ * - Arrays are represented as []-delimited, parsable JSON
68
+ * - Numbers and booleans are not quoted explicitly
69
+ */
68
70
function refOf < T extends string | number | boolean | string [ ] > ( arg : T | Expression < T > ) : string {
69
- return arg instanceof Expression ? arg . toString ( ) : quoteIfString ( arg ) . toString ( ) ;
71
+ if ( arg instanceof Expression ) {
72
+ return arg . toString ( ) ;
73
+ } else if ( typeof arg === "string" ) {
74
+ return `"${ arg } "` ;
75
+ } else if ( Array . isArray ( arg ) ) {
76
+ return JSON . stringify ( arg ) ;
77
+ } else {
78
+ return arg . toString ( ) ;
79
+ }
70
80
}
71
81
72
82
/**
@@ -123,9 +133,9 @@ export class CompareExpression<
123
133
const right = valueOf ( this . rhs ) ;
124
134
switch ( this . cmp ) {
125
135
case "==" :
126
- return left === right ;
136
+ return Array . isArray ( left ) ? this . arrayEquals ( left , right as string [ ] ) : left === right ;
127
137
case "!=" :
128
- return left !== right ;
138
+ return Array . isArray ( left ) ? ! this . arrayEquals ( left , right as string [ ] ) : left !== right ;
129
139
case ">" :
130
140
return left > right ;
131
141
case ">=" :
@@ -139,6 +149,11 @@ export class CompareExpression<
139
149
}
140
150
}
141
151
152
+ /** @internal */
153
+ arrayEquals ( a : string [ ] , b : string [ ] ) : boolean {
154
+ return a . every ( ( item ) => b . includes ( item ) ) && b . every ( ( item ) => a . includes ( item ) ) ;
155
+ }
156
+
142
157
toString ( ) {
143
158
const rhsStr = refOf ( this . rhs ) ;
144
159
return `${ this . lhs } ${ this . cmp } ${ rhsStr } ` ;
@@ -159,6 +174,7 @@ type ParamValueType = "string" | "list" | "boolean" | "int" | "float" | "secret"
159
174
type ParamInput < T > =
160
175
| { text : TextInput < T > }
161
176
| { select : SelectInput < T > }
177
+ | { multiSelect : MultiSelectInput }
162
178
| { resource : ResourceInput } ;
163
179
164
180
/**
@@ -201,6 +217,15 @@ export interface SelectInput<T = unknown> {
201
217
options : Array < SelectOptions < T > > ;
202
218
}
203
219
220
+ /**
221
+ * Specifies that a Param's value should be determined by having the user select
222
+ * a subset from a list of pre-canned options interactively at deploy-time.
223
+ * Will result in errors if used on Params of type other than string[].
224
+ */
225
+ export interface MultiSelectInput {
226
+ options : Array < SelectOptions < string > > ;
227
+ }
228
+
204
229
/**
205
230
* One of the options provided to a SelectInput, containing a value and
206
231
* optionally a human-readable label to display in the selection interface.
@@ -464,3 +489,44 @@ export class BooleanParam extends Param<boolean> {
464
489
return new TernaryExpression ( this , ifTrue , ifFalse ) ;
465
490
}
466
491
}
492
+
493
+ /**
494
+ * A parametrized value of String[] type that will be read from .env files
495
+ * if present, or prompted for by the CLI if missing.
496
+ */
497
+ export class ListParam extends Param < string [ ] > {
498
+ static type : ParamValueType = "list" ;
499
+
500
+ /** @internal */
501
+ runtimeValue ( ) : string [ ] {
502
+ const val = JSON . parse ( process . env [ this . name ] ) ;
503
+ if ( ! Array . isArray ( val ) || ! ( val as string [ ] ) . every ( ( v ) => typeof v === "string" ) ) {
504
+ return [ ] ;
505
+ }
506
+ return val as string [ ] ;
507
+ }
508
+
509
+ /** @hidden */
510
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
511
+ greaterThan ( rhs : string [ ] | Expression < string [ ] > ) : CompareExpression < string [ ] > {
512
+ throw new Error ( ">/< comparison operators not supported on params of type List" ) ;
513
+ }
514
+
515
+ /** @hidden */
516
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
517
+ greaterThanOrEqualTo ( rhs : string [ ] | Expression < string [ ] > ) : CompareExpression < string [ ] > {
518
+ throw new Error ( ">/< comparison operators not supported on params of type List" ) ;
519
+ }
520
+
521
+ /** @hidden */
522
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
523
+ lessThan ( rhs : string [ ] | Expression < string [ ] > ) : CompareExpression < string [ ] > {
524
+ throw new Error ( ">/< comparison operators not supported on params of type List" ) ;
525
+ }
526
+
527
+ /** @hidden */
528
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
529
+ lessThanorEqualTo ( rhs : string [ ] | Expression < string [ ] > ) : CompareExpression < string [ ] > {
530
+ throw new Error ( ">/< comparison operators not supported on params of type List" ) ;
531
+ }
532
+ }
0 commit comments