UNPKG

14.7 kBJavaScriptView Raw
1"use strict";
2// The MIT License (MIT)
3//
4// Copyright (c) 2021 Firebase
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in all
14// copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23Object.defineProperty(exports, "__esModule", { value: true });
24exports.ListParam = exports.BooleanParam = exports.FloatParam = exports.IntParam = exports.InternalExpression = exports.StringParam = exports.SecretParam = exports.Param = exports.BUCKET_PICKER = exports.multiSelect = exports.select = exports.CompareExpression = exports.TernaryExpression = exports.Expression = void 0;
25const logger = require("../logger");
26/*
27 * A CEL expression which can be evaluated during function deployment, and
28 * resolved to a value of the generic type parameter: i.e, you can pass
29 * an Expression<number> as the value of an option that normally accepts numbers.
30 */
31class Expression {
32 /** Returns the expression's runtime value, based on the CLI's resolution of parameters. */
33 value() {
34 if (process.env.FUNCTIONS_CONTROL_API === "true") {
35 logger.warn(`${this.toString()}.value() invoked during function deployment, instead of during runtime.`);
36 logger.warn(`This is usually a mistake. In configs, use Params directly without calling .value().`);
37 logger.warn(`example: { memory: memoryParam } not { memory: memoryParam.value() }`);
38 }
39 return this.runtimeValue();
40 }
41 /** @internal */
42 runtimeValue() {
43 throw new Error("Not implemented");
44 }
45 /** Returns the expression's representation as a braced CEL expression. */
46 toCEL() {
47 return `{{ ${this.toString()} }}`;
48 }
49 /** Returns the expression's representation as JSON. */
50 toJSON() {
51 return this.toString();
52 }
53}
54exports.Expression = Expression;
55function valueOf(arg) {
56 return arg instanceof Expression ? arg.runtimeValue() : arg;
57}
58/**
59 * Returns how an entity (either an `Expression` or a literal value) should be represented in CEL.
60 * - Expressions delegate to the `.toString()` method, which is used by the WireManifest
61 * - Strings have to be quoted explicitly
62 * - Arrays are represented as []-delimited, parsable JSON
63 * - Numbers and booleans are not quoted explicitly
64 */
65function refOf(arg) {
66 if (arg instanceof Expression) {
67 return arg.toString();
68 }
69 else if (typeof arg === "string") {
70 return `"${arg}"`;
71 }
72 else if (Array.isArray(arg)) {
73 return JSON.stringify(arg);
74 }
75 else {
76 return arg.toString();
77 }
78}
79/**
80 * A CEL expression corresponding to a ternary operator, e.g {{ cond ? ifTrue : ifFalse }}
81 */
82class TernaryExpression extends Expression {
83 constructor(test, ifTrue, ifFalse) {
84 super();
85 this.test = test;
86 this.ifTrue = ifTrue;
87 this.ifFalse = ifFalse;
88 this.ifTrue = ifTrue;
89 this.ifFalse = ifFalse;
90 }
91 /** @internal */
92 runtimeValue() {
93 return this.test.runtimeValue() ? valueOf(this.ifTrue) : valueOf(this.ifFalse);
94 }
95 toString() {
96 return `${this.test} ? ${refOf(this.ifTrue)} : ${refOf(this.ifFalse)}`;
97 }
98}
99exports.TernaryExpression = TernaryExpression;
100/**
101 * A CEL expression that evaluates to boolean true or false based on a comparison
102 * between the value of another expression and a literal of that same type.
103 */
104class CompareExpression extends Expression {
105 constructor(cmp, lhs, rhs) {
106 super();
107 this.cmp = cmp;
108 this.lhs = lhs;
109 this.rhs = rhs;
110 }
111 /** @internal */
112 runtimeValue() {
113 const left = this.lhs.runtimeValue();
114 const right = valueOf(this.rhs);
115 switch (this.cmp) {
116 case "==":
117 return Array.isArray(left) ? this.arrayEquals(left, right) : left === right;
118 case "!=":
119 return Array.isArray(left) ? !this.arrayEquals(left, right) : left !== right;
120 case ">":
121 return left > right;
122 case ">=":
123 return left >= right;
124 case "<":
125 return left < right;
126 case "<=":
127 return left <= right;
128 default:
129 throw new Error(`Unknown comparator ${this.cmp}`);
130 }
131 }
132 /** @internal */
133 arrayEquals(a, b) {
134 return a.every((item) => b.includes(item)) && b.every((item) => a.includes(item));
135 }
136 toString() {
137 const rhsStr = refOf(this.rhs);
138 return `${this.lhs} ${this.cmp} ${rhsStr}`;
139 }
140 /** Returns a `TernaryExpression` which can resolve to one of two values, based on the resolution of this comparison. */
141 thenElse(ifTrue, ifFalse) {
142 return new TernaryExpression(this, ifTrue, ifFalse);
143 }
144}
145exports.CompareExpression = CompareExpression;
146/** Create a select input from a series of values or a map of labels to values */
147function select(options) {
148 let wireOpts;
149 if (Array.isArray(options)) {
150 wireOpts = options.map((opt) => ({ value: opt }));
151 }
152 else {
153 wireOpts = Object.entries(options).map(([label, value]) => ({ label, value }));
154 }
155 return {
156 select: {
157 options: wireOpts,
158 },
159 };
160}
161exports.select = select;
162/** Create a multi-select input from a series of values or map of labels to values. */
163function multiSelect(options) {
164 let wireOpts;
165 if (Array.isArray(options)) {
166 wireOpts = options.map((opt) => ({ value: opt }));
167 }
168 else {
169 wireOpts = Object.entries(options).map(([label, value]) => ({ label, value }));
170 }
171 return {
172 multiSelect: {
173 options: wireOpts,
174 },
175 };
176}
177exports.multiSelect = multiSelect;
178/**
179 * Autogenerate a list of buckets in a project that a user can select from.
180 */
181exports.BUCKET_PICKER = {
182 resource: {
183 type: "storage.googleapis.com/Bucket",
184 },
185};
186/**
187 * Represents a parametrized value that will be read from .env files if present,
188 * or prompted for by the CLI if missing. Instantiate these with the defineX
189 * methods exported by the firebase-functions/params namespace.
190 */
191class Param extends Expression {
192 constructor(name, options = {}) {
193 super();
194 this.name = name;
195 this.options = options;
196 }
197 /** @internal */
198 runtimeValue() {
199 throw new Error("Not implemented");
200 }
201 /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */
202 cmp(cmp, rhs) {
203 return new CompareExpression(cmp, this, rhs);
204 }
205 /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */
206 equals(rhs) {
207 return this.cmp("==", rhs);
208 }
209 /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */
210 notEquals(rhs) {
211 return this.cmp("!=", rhs);
212 }
213 /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */
214 greaterThan(rhs) {
215 return this.cmp(">", rhs);
216 }
217 /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */
218 greaterThanOrEqualTo(rhs) {
219 return this.cmp(">=", rhs);
220 }
221 /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */
222 lessThan(rhs) {
223 return this.cmp("<", rhs);
224 }
225 /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */
226 lessThanOrEqualTo(rhs) {
227 return this.cmp("<=", rhs);
228 }
229 /**
230 * Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression.
231 * @deprecated A typo. Use lessThanOrEqualTo instead.
232 */
233 lessThanorEqualTo(rhs) {
234 return this.lessThanOrEqualTo(rhs);
235 }
236 toString() {
237 return `params.${this.name}`;
238 }
239 /** @internal */
240 toSpec() {
241 const { default: paramDefault, ...otherOptions } = this.options;
242 const out = {
243 name: this.name,
244 ...otherOptions,
245 type: this.constructor.type,
246 };
247 if (paramDefault instanceof Expression) {
248 out.default = paramDefault.toCEL();
249 }
250 else if (paramDefault !== undefined) {
251 out.default = paramDefault;
252 }
253 if (out.input && "text" in out.input && out.input.text.validationRegex instanceof RegExp) {
254 out.input.text.validationRegex = out.input.text.validationRegex.source;
255 }
256 return out;
257 }
258}
259exports.Param = Param;
260Param.type = "string";
261/**
262 * A parametrized string whose value is stored in Cloud Secret Manager
263 * instead of the local filesystem. Supply instances of SecretParams to
264 * the secrets array while defining a Function to make their values accessible
265 * during execution of that Function.
266 */
267class SecretParam {
268 constructor(name) {
269 this.name = name;
270 }
271 /** @internal */
272 runtimeValue() {
273 const val = process.env[this.name];
274 if (val === undefined) {
275 logger.warn(`No value found for secret parameter "${this.name}". A function can only access a secret if you include the secret in the function's dependency array.`);
276 }
277 return val || "";
278 }
279 /** @internal */
280 toSpec() {
281 return {
282 type: "secret",
283 name: this.name,
284 };
285 }
286 /** Returns the secret's value at runtime. Throws an error if accessed during deployment. */
287 value() {
288 if (process.env.FUNCTIONS_CONTROL_API === "true") {
289 throw new Error(`Cannot access the value of secret "${this.name}" during function deployment. Secret values are only available at runtime.`);
290 }
291 return this.runtimeValue();
292 }
293}
294exports.SecretParam = SecretParam;
295SecretParam.type = "secret";
296/**
297 * A parametrized value of String type that will be read from .env files
298 * if present, or prompted for by the CLI if missing.
299 */
300class StringParam extends Param {
301 /** @internal */
302 runtimeValue() {
303 return process.env[this.name] || "";
304 }
305}
306exports.StringParam = StringParam;
307/**
308 * A CEL expression which represents an internal Firebase variable. This class
309 * cannot be instantiated by developers, but we provide several canned instances
310 * of it to make available parameters that will never have to be defined at
311 * deployment time, and can always be read from process.env.
312 * @internal
313 */
314class InternalExpression extends Param {
315 constructor(name, getter) {
316 super(name);
317 this.getter = getter;
318 }
319 /** @internal */
320 runtimeValue() {
321 return this.getter(process.env) || "";
322 }
323 toSpec() {
324 throw new Error("An InternalExpression should never be marshalled for wire transmission.");
325 }
326}
327exports.InternalExpression = InternalExpression;
328/**
329 * A parametrized value of Integer type that will be read from .env files
330 * if present, or prompted for by the CLI if missing.
331 */
332class IntParam extends Param {
333 /** @internal */
334 runtimeValue() {
335 return parseInt(process.env[this.name] || "0", 10) || 0;
336 }
337}
338exports.IntParam = IntParam;
339IntParam.type = "int";
340/**
341 * A parametrized value of Float type that will be read from .env files
342 * if present, or prompted for by the CLI if missing.
343 */
344class FloatParam extends Param {
345 /** @internal */
346 runtimeValue() {
347 return parseFloat(process.env[this.name] || "0") || 0;
348 }
349}
350exports.FloatParam = FloatParam;
351FloatParam.type = "float";
352/**
353 * A parametrized value of Boolean type that will be read from .env files
354 * if present, or prompted for by the CLI if missing.
355 */
356class BooleanParam extends Param {
357 /** @internal */
358 runtimeValue() {
359 return !!process.env[this.name] && process.env[this.name] === "true";
360 }
361 /** @deprecated */
362 then(ifTrue, ifFalse) {
363 return this.thenElse(ifTrue, ifFalse);
364 }
365 thenElse(ifTrue, ifFalse) {
366 return new TernaryExpression(this, ifTrue, ifFalse);
367 }
368}
369exports.BooleanParam = BooleanParam;
370BooleanParam.type = "boolean";
371/**
372 * A parametrized value of String[] type that will be read from .env files
373 * if present, or prompted for by the CLI if missing.
374 */
375class ListParam extends Param {
376 /** @internal */
377 runtimeValue() {
378 const val = JSON.parse(process.env[this.name]);
379 if (!Array.isArray(val) || !val.every((v) => typeof v === "string")) {
380 return [];
381 }
382 return val;
383 }
384 /** @hidden */
385 // eslint-disable-next-line @typescript-eslint/no-unused-vars
386 greaterThan(rhs) {
387 throw new Error(">/< comparison operators not supported on params of type List");
388 }
389 /** @hidden */
390 // eslint-disable-next-line @typescript-eslint/no-unused-vars
391 greaterThanOrEqualTo(rhs) {
392 throw new Error(">/< comparison operators not supported on params of type List");
393 }
394 /** @hidden */
395 // eslint-disable-next-line @typescript-eslint/no-unused-vars
396 lessThan(rhs) {
397 throw new Error(">/< comparison operators not supported on params of type List");
398 }
399 /** @hidden */
400 // eslint-disable-next-line @typescript-eslint/no-unused-vars
401 lessThanorEqualTo(rhs) {
402 throw new Error(">/< comparison operators not supported on params of type List");
403 }
404}
405exports.ListParam = ListParam;
406ListParam.type = "list";