No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

build.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. 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;
  4. const backend = require("./backend");
  5. const proto = require("../../gcp/proto");
  6. const api = require("../../.../../api");
  7. const params = require("./params");
  8. const experiments = require("../../experiments");
  9. const error_1 = require("../../error");
  10. const functional_1 = require("../../functional");
  11. const env_1 = require("../../functions/env");
  12. function empty() {
  13. return {
  14. requiredAPIs: [],
  15. endpoints: {},
  16. params: [],
  17. };
  18. }
  19. exports.empty = empty;
  20. function of(endpoints) {
  21. const build = empty();
  22. build.endpoints = endpoints;
  23. return build;
  24. }
  25. exports.of = of;
  26. function isHttpsTriggered(triggered) {
  27. return {}.hasOwnProperty.call(triggered, "httpsTrigger");
  28. }
  29. exports.isHttpsTriggered = isHttpsTriggered;
  30. function isCallableTriggered(triggered) {
  31. return {}.hasOwnProperty.call(triggered, "callableTrigger");
  32. }
  33. exports.isCallableTriggered = isCallableTriggered;
  34. function isEventTriggered(triggered) {
  35. return {}.hasOwnProperty.call(triggered, "eventTrigger");
  36. }
  37. exports.isEventTriggered = isEventTriggered;
  38. function isScheduleTriggered(triggered) {
  39. return {}.hasOwnProperty.call(triggered, "scheduleTrigger");
  40. }
  41. exports.isScheduleTriggered = isScheduleTriggered;
  42. function isTaskQueueTriggered(triggered) {
  43. return {}.hasOwnProperty.call(triggered, "taskQueueTrigger");
  44. }
  45. exports.isTaskQueueTriggered = isTaskQueueTriggered;
  46. function isBlockingTriggered(triggered) {
  47. return {}.hasOwnProperty.call(triggered, "blockingTrigger");
  48. }
  49. exports.isBlockingTriggered = isBlockingTriggered;
  50. const allMemoryOptions = [128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768];
  51. function isValidMemoryOption(mem) {
  52. return allMemoryOptions.includes(mem);
  53. }
  54. exports.isValidMemoryOption = isValidMemoryOption;
  55. exports.AllFunctionsPlatforms = ["gcfv1", "gcfv2"];
  56. exports.AllVpcEgressSettings = ["PRIVATE_RANGES_ONLY", "ALL_TRAFFIC"];
  57. exports.AllIngressSettings = [
  58. "ALLOW_ALL",
  59. "ALLOW_INTERNAL_ONLY",
  60. "ALLOW_INTERNAL_AND_GCLB",
  61. ];
  62. async function resolveBackend(build, firebaseConfig, userEnvOpt, userEnvs, nonInteractive) {
  63. let paramValues = {};
  64. if (experiments.isEnabled("functionsparams")) {
  65. paramValues = await params.resolveParams(build.params, firebaseConfig, envWithTypes(build.params, userEnvs), nonInteractive);
  66. const toWrite = {};
  67. for (const paramName of Object.keys(paramValues)) {
  68. const paramValue = paramValues[paramName];
  69. if (Object.prototype.hasOwnProperty.call(userEnvs, paramName) || paramValue.internal) {
  70. continue;
  71. }
  72. toWrite[paramName] = paramValue.toString();
  73. }
  74. (0, env_1.writeUserEnvs)(toWrite, userEnvOpt);
  75. }
  76. return { backend: toBackend(build, paramValues), envs: paramValues };
  77. }
  78. exports.resolveBackend = resolveBackend;
  79. function envWithTypes(definedParams, rawEnvs) {
  80. const out = {};
  81. for (const envName of Object.keys(rawEnvs)) {
  82. const value = rawEnvs[envName];
  83. let providedType = {
  84. string: true,
  85. boolean: true,
  86. number: true,
  87. list: true,
  88. };
  89. for (const param of definedParams) {
  90. if (param.name === envName) {
  91. if (param.type === "string") {
  92. providedType = {
  93. string: true,
  94. boolean: false,
  95. number: false,
  96. list: false,
  97. };
  98. }
  99. else if (param.type === "int") {
  100. providedType = {
  101. string: false,
  102. boolean: false,
  103. number: true,
  104. list: false,
  105. };
  106. }
  107. else if (param.type === "boolean") {
  108. providedType = {
  109. string: false,
  110. boolean: true,
  111. number: false,
  112. list: false,
  113. };
  114. }
  115. else if (param.type === "list") {
  116. providedType = {
  117. string: false,
  118. boolean: false,
  119. number: false,
  120. list: true,
  121. };
  122. }
  123. }
  124. }
  125. out[envName] = new params.ParamValue(value, false, providedType);
  126. }
  127. return out;
  128. }
  129. class Resolver {
  130. constructor(paramValues) {
  131. this.paramValues = paramValues;
  132. this.resolveInt = (i) => {
  133. if (i === null) {
  134. return i;
  135. }
  136. return params.resolveInt(i, this.paramValues);
  137. };
  138. this.resolveBoolean = (i) => {
  139. if (i === null) {
  140. return i;
  141. }
  142. return params.resolveBoolean(i, this.paramValues);
  143. };
  144. this.resolveString = (i) => {
  145. if (i === null) {
  146. return i;
  147. }
  148. return params.resolveString(i, this.paramValues);
  149. };
  150. }
  151. resolveStrings(dest, src, ...keys) {
  152. for (const key of keys) {
  153. const orig = src[key];
  154. if (typeof orig === "undefined") {
  155. continue;
  156. }
  157. dest[key] = orig === null ? null : params.resolveString(orig, this.paramValues);
  158. }
  159. }
  160. resolveInts(dest, src, ...keys) {
  161. for (const key of keys) {
  162. const orig = src[key];
  163. if (typeof orig === "undefined") {
  164. continue;
  165. }
  166. dest[key] = orig === null ? null : params.resolveInt(orig, this.paramValues);
  167. }
  168. }
  169. }
  170. function toBackend(build, paramValues) {
  171. const r = new Resolver(paramValues);
  172. const bkEndpoints = [];
  173. for (const endpointId of Object.keys(build.endpoints)) {
  174. const bdEndpoint = build.endpoints[endpointId];
  175. if (r.resolveBoolean(bdEndpoint.omit || false)) {
  176. continue;
  177. }
  178. let regions = [];
  179. if (!bdEndpoint.region) {
  180. regions = [api.functionsDefaultRegion];
  181. }
  182. else {
  183. regions = params.resolveList(bdEndpoint.region, paramValues);
  184. }
  185. for (const region of regions) {
  186. const trigger = discoverTrigger(bdEndpoint, region, r);
  187. if (typeof bdEndpoint.platform === "undefined") {
  188. throw new error_1.FirebaseError("platform can't be undefined");
  189. }
  190. const bkEndpoint = Object.assign({ id: endpointId, project: bdEndpoint.project, region: region, entryPoint: bdEndpoint.entryPoint, platform: bdEndpoint.platform, runtime: bdEndpoint.runtime }, trigger);
  191. proto.copyIfPresent(bkEndpoint, bdEndpoint, "environmentVariables", "labels", "secretEnvironmentVariables", "serviceAccount");
  192. proto.convertIfPresent(bkEndpoint, bdEndpoint, "ingressSettings", (from) => {
  193. if (from !== null && !backend.AllIngressSettings.includes(from)) {
  194. throw new error_1.FirebaseError(`Cannot set ingress settings to invalid value ${from}`);
  195. }
  196. return from;
  197. });
  198. proto.convertIfPresent(bkEndpoint, bdEndpoint, "availableMemoryMb", (from) => {
  199. const mem = r.resolveInt(from);
  200. if (mem !== null && !backend.isValidMemoryOption(mem)) {
  201. throw new error_1.FirebaseError(`Function memory (${mem}) must resolve to a supported value, if present: ${JSON.stringify(allMemoryOptions)}`);
  202. }
  203. return mem || null;
  204. });
  205. r.resolveInts(bkEndpoint, bdEndpoint, "timeoutSeconds", "maxInstances", "minInstances", "concurrency");
  206. proto.convertIfPresent(bkEndpoint, bdEndpoint, "cpu", (0, functional_1.nullsafeVisitor)((cpu) => (cpu === "gcf_gen1" ? cpu : r.resolveInt(cpu))));
  207. if (bdEndpoint.vpc) {
  208. if (bdEndpoint.vpc.connector && !bdEndpoint.vpc.connector.includes("/")) {
  209. bdEndpoint.vpc.connector = `projects/${bdEndpoint.project}/locations/${region}/connectors/${bdEndpoint.vpc.connector}`;
  210. }
  211. bkEndpoint.vpc = { connector: params.resolveString(bdEndpoint.vpc.connector, paramValues) };
  212. proto.copyIfPresent(bkEndpoint.vpc, bdEndpoint.vpc, "egressSettings");
  213. }
  214. else if (bdEndpoint.vpc === null) {
  215. bkEndpoint.vpc = null;
  216. }
  217. bkEndpoints.push(bkEndpoint);
  218. }
  219. }
  220. const bkend = backend.of(...bkEndpoints);
  221. bkend.requiredAPIs = build.requiredAPIs;
  222. return bkend;
  223. }
  224. exports.toBackend = toBackend;
  225. function discoverTrigger(endpoint, region, r) {
  226. if (isHttpsTriggered(endpoint)) {
  227. const httpsTrigger = {};
  228. if (endpoint.httpsTrigger.invoker === null) {
  229. httpsTrigger.invoker = null;
  230. }
  231. else if (typeof endpoint.httpsTrigger.invoker !== "undefined") {
  232. httpsTrigger.invoker = endpoint.httpsTrigger.invoker.map(r.resolveString);
  233. }
  234. return { httpsTrigger };
  235. }
  236. else if (isCallableTriggered(endpoint)) {
  237. return { callableTrigger: {} };
  238. }
  239. else if (isBlockingTriggered(endpoint)) {
  240. return { blockingTrigger: endpoint.blockingTrigger };
  241. }
  242. else if (isEventTriggered(endpoint)) {
  243. const eventTrigger = {
  244. eventType: endpoint.eventTrigger.eventType,
  245. retry: r.resolveBoolean(endpoint.eventTrigger.retry) || false,
  246. };
  247. if (endpoint.eventTrigger.eventFilters) {
  248. eventTrigger.eventFilters = (0, functional_1.mapObject)(endpoint.eventTrigger.eventFilters, r.resolveString);
  249. }
  250. if (endpoint.eventTrigger.eventFilterPathPatterns) {
  251. eventTrigger.eventFilterPathPatterns = (0, functional_1.mapObject)(endpoint.eventTrigger.eventFilterPathPatterns, r.resolveString);
  252. }
  253. r.resolveStrings(eventTrigger, endpoint.eventTrigger, "serviceAccount", "region", "channel");
  254. return { eventTrigger };
  255. }
  256. else if (isScheduleTriggered(endpoint)) {
  257. const bkSchedule = {
  258. schedule: r.resolveString(endpoint.scheduleTrigger.schedule),
  259. };
  260. if (endpoint.scheduleTrigger.timeZone !== undefined) {
  261. bkSchedule.timeZone = r.resolveString(endpoint.scheduleTrigger.timeZone);
  262. }
  263. if (endpoint.scheduleTrigger.retryConfig) {
  264. const bkRetry = {};
  265. r.resolveInts(bkRetry, endpoint.scheduleTrigger.retryConfig, "maxBackoffSeconds", "minBackoffSeconds", "maxRetrySeconds", "retryCount", "maxDoublings");
  266. bkSchedule.retryConfig = bkRetry;
  267. }
  268. else if (endpoint.scheduleTrigger.retryConfig === null) {
  269. bkSchedule.retryConfig = null;
  270. }
  271. return { scheduleTrigger: bkSchedule };
  272. }
  273. else if ("taskQueueTrigger" in endpoint) {
  274. const taskQueueTrigger = {};
  275. if (endpoint.taskQueueTrigger.rateLimits) {
  276. taskQueueTrigger.rateLimits = {};
  277. r.resolveInts(taskQueueTrigger.rateLimits, endpoint.taskQueueTrigger.rateLimits, "maxConcurrentDispatches", "maxDispatchesPerSecond");
  278. }
  279. else if (endpoint.taskQueueTrigger.rateLimits === null) {
  280. taskQueueTrigger.rateLimits = null;
  281. }
  282. if (endpoint.taskQueueTrigger.retryConfig) {
  283. taskQueueTrigger.retryConfig = {};
  284. r.resolveInts(taskQueueTrigger.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxBackoffSeconds", "minBackoffSeconds", "maxRetrySeconds", "maxDoublings");
  285. }
  286. else if (endpoint.taskQueueTrigger.retryConfig === null) {
  287. taskQueueTrigger.retryConfig = null;
  288. }
  289. if (endpoint.taskQueueTrigger.invoker) {
  290. taskQueueTrigger.invoker = endpoint.taskQueueTrigger.invoker.map(r.resolveString);
  291. }
  292. else if (endpoint.taskQueueTrigger.invoker === null) {
  293. taskQueueTrigger.invoker = null;
  294. }
  295. return { taskQueueTrigger };
  296. }
  297. (0, functional_1.assertExhaustive)(endpoint);
  298. }