"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.toBackend = exports.resolveBackend = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.AllFunctionsPlatforms = exports.isValidMemoryOption = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.of = exports.empty = void 0; const backend = require("./backend"); const proto = require("../../gcp/proto"); const api = require("../../.../../api"); const params = require("./params"); const experiments = require("../../experiments"); const error_1 = require("../../error"); const functional_1 = require("../../functional"); const env_1 = require("../../functions/env"); function empty() { return { requiredAPIs: [], endpoints: {}, params: [], }; } exports.empty = empty; function of(endpoints) { const build = empty(); build.endpoints = endpoints; return build; } exports.of = of; function isHttpsTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "httpsTrigger"); } exports.isHttpsTriggered = isHttpsTriggered; function isCallableTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "callableTrigger"); } exports.isCallableTriggered = isCallableTriggered; function isEventTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "eventTrigger"); } exports.isEventTriggered = isEventTriggered; function isScheduleTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "scheduleTrigger"); } exports.isScheduleTriggered = isScheduleTriggered; function isTaskQueueTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "taskQueueTrigger"); } exports.isTaskQueueTriggered = isTaskQueueTriggered; function isBlockingTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "blockingTrigger"); } exports.isBlockingTriggered = isBlockingTriggered; const allMemoryOptions = [128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768]; function isValidMemoryOption(mem) { return allMemoryOptions.includes(mem); } exports.isValidMemoryOption = isValidMemoryOption; exports.AllFunctionsPlatforms = ["gcfv1", "gcfv2"]; exports.AllVpcEgressSettings = ["PRIVATE_RANGES_ONLY", "ALL_TRAFFIC"]; exports.AllIngressSettings = [ "ALLOW_ALL", "ALLOW_INTERNAL_ONLY", "ALLOW_INTERNAL_AND_GCLB", ]; async function resolveBackend(build, firebaseConfig, userEnvOpt, userEnvs, nonInteractive) { let paramValues = {}; if (experiments.isEnabled("functionsparams")) { paramValues = await params.resolveParams(build.params, firebaseConfig, envWithTypes(build.params, userEnvs), nonInteractive); const toWrite = {}; for (const paramName of Object.keys(paramValues)) { const paramValue = paramValues[paramName]; if (Object.prototype.hasOwnProperty.call(userEnvs, paramName) || paramValue.internal) { continue; } toWrite[paramName] = paramValue.toString(); } (0, env_1.writeUserEnvs)(toWrite, userEnvOpt); } return { backend: toBackend(build, paramValues), envs: paramValues }; } exports.resolveBackend = resolveBackend; function envWithTypes(definedParams, rawEnvs) { const out = {}; for (const envName of Object.keys(rawEnvs)) { const value = rawEnvs[envName]; let providedType = { string: true, boolean: true, number: true, list: true, }; for (const param of definedParams) { if (param.name === envName) { if (param.type === "string") { providedType = { string: true, boolean: false, number: false, list: false, }; } else if (param.type === "int") { providedType = { string: false, boolean: false, number: true, list: false, }; } else if (param.type === "boolean") { providedType = { string: false, boolean: true, number: false, list: false, }; } else if (param.type === "list") { providedType = { string: false, boolean: false, number: false, list: true, }; } } } out[envName] = new params.ParamValue(value, false, providedType); } return out; } class Resolver { constructor(paramValues) { this.paramValues = paramValues; this.resolveInt = (i) => { if (i === null) { return i; } return params.resolveInt(i, this.paramValues); }; this.resolveBoolean = (i) => { if (i === null) { return i; } return params.resolveBoolean(i, this.paramValues); }; this.resolveString = (i) => { if (i === null) { return i; } return params.resolveString(i, this.paramValues); }; } resolveStrings(dest, src, ...keys) { for (const key of keys) { const orig = src[key]; if (typeof orig === "undefined") { continue; } dest[key] = orig === null ? null : params.resolveString(orig, this.paramValues); } } resolveInts(dest, src, ...keys) { for (const key of keys) { const orig = src[key]; if (typeof orig === "undefined") { continue; } dest[key] = orig === null ? null : params.resolveInt(orig, this.paramValues); } } } function toBackend(build, paramValues) { const r = new Resolver(paramValues); const bkEndpoints = []; for (const endpointId of Object.keys(build.endpoints)) { const bdEndpoint = build.endpoints[endpointId]; if (r.resolveBoolean(bdEndpoint.omit || false)) { continue; } let regions = []; if (!bdEndpoint.region) { regions = [api.functionsDefaultRegion]; } else { regions = params.resolveList(bdEndpoint.region, paramValues); } for (const region of regions) { const trigger = discoverTrigger(bdEndpoint, region, r); if (typeof bdEndpoint.platform === "undefined") { throw new error_1.FirebaseError("platform can't be undefined"); } const bkEndpoint = Object.assign({ id: endpointId, project: bdEndpoint.project, region: region, entryPoint: bdEndpoint.entryPoint, platform: bdEndpoint.platform, runtime: bdEndpoint.runtime }, trigger); proto.copyIfPresent(bkEndpoint, bdEndpoint, "environmentVariables", "labels", "secretEnvironmentVariables", "serviceAccount"); proto.convertIfPresent(bkEndpoint, bdEndpoint, "ingressSettings", (from) => { if (from !== null && !backend.AllIngressSettings.includes(from)) { throw new error_1.FirebaseError(`Cannot set ingress settings to invalid value ${from}`); } return from; }); proto.convertIfPresent(bkEndpoint, bdEndpoint, "availableMemoryMb", (from) => { const mem = r.resolveInt(from); if (mem !== null && !backend.isValidMemoryOption(mem)) { throw new error_1.FirebaseError(`Function memory (${mem}) must resolve to a supported value, if present: ${JSON.stringify(allMemoryOptions)}`); } return mem || null; }); r.resolveInts(bkEndpoint, bdEndpoint, "timeoutSeconds", "maxInstances", "minInstances", "concurrency"); proto.convertIfPresent(bkEndpoint, bdEndpoint, "cpu", (0, functional_1.nullsafeVisitor)((cpu) => (cpu === "gcf_gen1" ? cpu : r.resolveInt(cpu)))); if (bdEndpoint.vpc) { if (bdEndpoint.vpc.connector && !bdEndpoint.vpc.connector.includes("/")) { bdEndpoint.vpc.connector = `projects/${bdEndpoint.project}/locations/${region}/connectors/${bdEndpoint.vpc.connector}`; } bkEndpoint.vpc = { connector: params.resolveString(bdEndpoint.vpc.connector, paramValues) }; proto.copyIfPresent(bkEndpoint.vpc, bdEndpoint.vpc, "egressSettings"); } else if (bdEndpoint.vpc === null) { bkEndpoint.vpc = null; } bkEndpoints.push(bkEndpoint); } } const bkend = backend.of(...bkEndpoints); bkend.requiredAPIs = build.requiredAPIs; return bkend; } exports.toBackend = toBackend; function discoverTrigger(endpoint, region, r) { if (isHttpsTriggered(endpoint)) { const httpsTrigger = {}; if (endpoint.httpsTrigger.invoker === null) { httpsTrigger.invoker = null; } else if (typeof endpoint.httpsTrigger.invoker !== "undefined") { httpsTrigger.invoker = endpoint.httpsTrigger.invoker.map(r.resolveString); } return { httpsTrigger }; } else if (isCallableTriggered(endpoint)) { return { callableTrigger: {} }; } else if (isBlockingTriggered(endpoint)) { return { blockingTrigger: endpoint.blockingTrigger }; } else if (isEventTriggered(endpoint)) { const eventTrigger = { eventType: endpoint.eventTrigger.eventType, retry: r.resolveBoolean(endpoint.eventTrigger.retry) || false, }; if (endpoint.eventTrigger.eventFilters) { eventTrigger.eventFilters = (0, functional_1.mapObject)(endpoint.eventTrigger.eventFilters, r.resolveString); } if (endpoint.eventTrigger.eventFilterPathPatterns) { eventTrigger.eventFilterPathPatterns = (0, functional_1.mapObject)(endpoint.eventTrigger.eventFilterPathPatterns, r.resolveString); } r.resolveStrings(eventTrigger, endpoint.eventTrigger, "serviceAccount", "region", "channel"); return { eventTrigger }; } else if (isScheduleTriggered(endpoint)) { const bkSchedule = { schedule: r.resolveString(endpoint.scheduleTrigger.schedule), }; if (endpoint.scheduleTrigger.timeZone !== undefined) { bkSchedule.timeZone = r.resolveString(endpoint.scheduleTrigger.timeZone); } if (endpoint.scheduleTrigger.retryConfig) { const bkRetry = {}; r.resolveInts(bkRetry, endpoint.scheduleTrigger.retryConfig, "maxBackoffSeconds", "minBackoffSeconds", "maxRetrySeconds", "retryCount", "maxDoublings"); bkSchedule.retryConfig = bkRetry; } else if (endpoint.scheduleTrigger.retryConfig === null) { bkSchedule.retryConfig = null; } return { scheduleTrigger: bkSchedule }; } else if ("taskQueueTrigger" in endpoint) { const taskQueueTrigger = {}; if (endpoint.taskQueueTrigger.rateLimits) { taskQueueTrigger.rateLimits = {}; r.resolveInts(taskQueueTrigger.rateLimits, endpoint.taskQueueTrigger.rateLimits, "maxConcurrentDispatches", "maxDispatchesPerSecond"); } else if (endpoint.taskQueueTrigger.rateLimits === null) { taskQueueTrigger.rateLimits = null; } if (endpoint.taskQueueTrigger.retryConfig) { taskQueueTrigger.retryConfig = {}; r.resolveInts(taskQueueTrigger.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxBackoffSeconds", "minBackoffSeconds", "maxRetrySeconds", "maxDoublings"); } else if (endpoint.taskQueueTrigger.retryConfig === null) { taskQueueTrigger.retryConfig = null; } if (endpoint.taskQueueTrigger.invoker) { taskQueueTrigger.invoker = endpoint.taskQueueTrigger.invoker.map(r.resolveString); } else if (endpoint.taskQueueTrigger.invoker === null) { taskQueueTrigger.invoker = null; } return { taskQueueTrigger }; } (0, functional_1.assertExhaustive)(endpoint); }