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.

v1alpha1.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.buildFromV1Alpha1 = void 0;
  4. const build = require("../../build");
  5. const proto_1 = require("../../../../gcp/proto");
  6. const parsing_1 = require("./parsing");
  7. const error_1 = require("../../../../error");
  8. const functional_1 = require("../../../../functional");
  9. const CHANNEL_NAME_REGEX = new RegExp("(projects\\/" +
  10. "(?<project>(?:\\d+)|(?:[A-Za-z]+[A-Za-z\\d-]*[A-Za-z\\d]?))\\/)?" +
  11. "locations\\/" +
  12. "(?<location>[A-Za-z\\d\\-_]+)\\/" +
  13. "channels\\/" +
  14. "(?<channel>[A-Za-z\\d\\-_]+)");
  15. function buildFromV1Alpha1(yaml, project, region, runtime) {
  16. const manifest = JSON.parse(JSON.stringify(yaml));
  17. (0, parsing_1.requireKeys)("", manifest, "endpoints");
  18. (0, parsing_1.assertKeyTypes)("", manifest, {
  19. specVersion: "string",
  20. params: "array",
  21. requiredAPIs: "array",
  22. endpoints: "object",
  23. });
  24. const bd = build.empty();
  25. bd.params = manifest.params || [];
  26. bd.requiredAPIs = parseRequiredAPIs(manifest);
  27. for (const id of Object.keys(manifest.endpoints)) {
  28. const me = manifest.endpoints[id];
  29. assertBuildEndpoint(me, id);
  30. const be = parseEndpointForBuild(id, me, project, region, runtime);
  31. bd.endpoints[id] = be;
  32. }
  33. return bd;
  34. }
  35. exports.buildFromV1Alpha1 = buildFromV1Alpha1;
  36. function parseRequiredAPIs(manifest) {
  37. const requiredAPIs = manifest.requiredAPIs || [];
  38. for (const { api, reason } of requiredAPIs) {
  39. if (typeof api !== "string") {
  40. throw new error_1.FirebaseError(`Invalid api "${JSON.stringify(api)}. Expected string`);
  41. }
  42. if (typeof reason !== "string") {
  43. throw new error_1.FirebaseError(`Invalid reason "${JSON.stringify(reason)} for API ${api}. Expected string`);
  44. }
  45. }
  46. return requiredAPIs;
  47. }
  48. function assertBuildEndpoint(ep, id) {
  49. const prefix = `endpoints[${id}]`;
  50. (0, parsing_1.assertKeyTypes)(prefix, ep, {
  51. region: "List",
  52. platform: (platform) => build.AllFunctionsPlatforms.includes(platform),
  53. entryPoint: "string",
  54. omit: "Field<boolean>?",
  55. availableMemoryMb: (mem) => mem === null || isCEL(mem) || build.isValidMemoryOption(mem),
  56. maxInstances: "Field<number>?",
  57. minInstances: "Field<number>?",
  58. concurrency: "Field<number>?",
  59. serviceAccount: "string?",
  60. serviceAccountEmail: "string?",
  61. timeoutSeconds: "Field<number>?",
  62. vpc: "object?",
  63. labels: "object?",
  64. ingressSettings: (setting) => setting === null || build.AllIngressSettings.includes(setting),
  65. environmentVariables: "object?",
  66. secretEnvironmentVariables: "array?",
  67. httpsTrigger: "object",
  68. callableTrigger: "object",
  69. eventTrigger: "object",
  70. scheduleTrigger: "object",
  71. taskQueueTrigger: "object",
  72. blockingTrigger: "object",
  73. cpu: (cpu) => cpu === null || isCEL(cpu) || cpu === "gcf_gen1" || typeof cpu === "number",
  74. });
  75. if (ep.vpc) {
  76. (0, parsing_1.assertKeyTypes)(prefix + ".vpc", ep.vpc, {
  77. connector: "string",
  78. egressSettings: (setting) => setting === null || build.AllVpcEgressSettings.includes(setting),
  79. });
  80. (0, parsing_1.requireKeys)(prefix + ".vpc", ep.vpc, "connector");
  81. }
  82. let triggerCount = 0;
  83. if (ep.httpsTrigger) {
  84. triggerCount++;
  85. }
  86. if (ep.callableTrigger) {
  87. triggerCount++;
  88. }
  89. if (ep.eventTrigger) {
  90. triggerCount++;
  91. }
  92. if (ep.scheduleTrigger) {
  93. triggerCount++;
  94. }
  95. if (ep.taskQueueTrigger) {
  96. triggerCount++;
  97. }
  98. if (ep.blockingTrigger) {
  99. triggerCount++;
  100. }
  101. if (!triggerCount) {
  102. throw new error_1.FirebaseError("Expected trigger in endpoint " + id);
  103. }
  104. if (triggerCount > 1) {
  105. throw new error_1.FirebaseError("Multiple triggers defined for endpoint" + id);
  106. }
  107. if (build.isEventTriggered(ep)) {
  108. (0, parsing_1.requireKeys)(prefix + ".eventTrigger", ep.eventTrigger, "eventType", "eventFilters");
  109. (0, parsing_1.assertKeyTypes)(prefix + ".eventTrigger", ep.eventTrigger, {
  110. eventFilters: "object",
  111. eventFilterPathPatterns: "object",
  112. eventType: "string",
  113. retry: "Field<boolean>",
  114. region: "Field<string>",
  115. serviceAccount: "string?",
  116. serviceAccountEmail: "string?",
  117. channel: "string",
  118. });
  119. }
  120. else if (build.isHttpsTriggered(ep)) {
  121. (0, parsing_1.assertKeyTypes)(prefix + ".httpsTrigger", ep.httpsTrigger, {
  122. invoker: "array?",
  123. });
  124. }
  125. else if (build.isCallableTriggered(ep)) {
  126. }
  127. else if (build.isScheduleTriggered(ep)) {
  128. (0, parsing_1.assertKeyTypes)(prefix + ".scheduleTrigger", ep.scheduleTrigger, {
  129. schedule: "Field<string>",
  130. timeZone: "Field<string>?",
  131. retryConfig: "object?",
  132. });
  133. if (ep.scheduleTrigger.retryConfig) {
  134. (0, parsing_1.assertKeyTypes)(prefix + ".scheduleTrigger.retryConfig", ep.scheduleTrigger.retryConfig, {
  135. retryCount: "Field<number>?",
  136. maxDoublings: "Field<number>?",
  137. minBackoffSeconds: "Field<number>?",
  138. maxBackoffSeconds: "Field<number>?",
  139. maxRetrySeconds: "Field<number>?",
  140. maxRetryDuration: "string?",
  141. minBackoffDuration: "string?",
  142. maxBackoffDuration: "string?",
  143. });
  144. }
  145. }
  146. else if (build.isTaskQueueTriggered(ep)) {
  147. (0, parsing_1.assertKeyTypes)(prefix + ".taskQueueTrigger", ep.taskQueueTrigger, {
  148. rateLimits: "object?",
  149. retryConfig: "object?",
  150. invoker: "array?",
  151. });
  152. if (ep.taskQueueTrigger.rateLimits) {
  153. (0, parsing_1.assertKeyTypes)(prefix + ".taskQueueTrigger.rateLimits", ep.taskQueueTrigger.rateLimits, {
  154. maxConcurrentDispatches: "Field<number>?",
  155. maxDispatchesPerSecond: "Field<number>?",
  156. });
  157. }
  158. if (ep.taskQueueTrigger.retryConfig) {
  159. (0, parsing_1.assertKeyTypes)(prefix + ".taskQueueTrigger.retryConfig", ep.taskQueueTrigger.retryConfig, {
  160. maxAttempts: "Field<number>?",
  161. maxRetrySeconds: "Field<number>?",
  162. minBackoffSeconds: "Field<number>?",
  163. maxBackoffSeconds: "Field<number>?",
  164. maxDoublings: "Field<number>?",
  165. });
  166. }
  167. }
  168. else if (build.isBlockingTriggered(ep)) {
  169. (0, parsing_1.requireKeys)(prefix + ".blockingTrigger", ep.blockingTrigger, "eventType");
  170. (0, parsing_1.assertKeyTypes)(prefix + ".blockingTrigger", ep.blockingTrigger, {
  171. eventType: "string",
  172. options: "object",
  173. });
  174. }
  175. else {
  176. throw new error_1.FirebaseError(`Do not recognize trigger type for endpoint ${id}. Try upgrading ` +
  177. "firebase-tools with npm install -g firebase-tools@latest");
  178. }
  179. }
  180. function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
  181. var _a;
  182. let triggered;
  183. if (build.isEventTriggered(ep)) {
  184. const eventTrigger = {
  185. eventType: ep.eventTrigger.eventType,
  186. retry: ep.eventTrigger.retry,
  187. };
  188. if ("serviceAccountEmail" in ep.eventTrigger) {
  189. eventTrigger.serviceAccount = ep.eventTrigger.serviceAccountEmail;
  190. }
  191. (0, proto_1.copyIfPresent)(eventTrigger, ep.eventTrigger, "serviceAccount", "eventFilterPathPatterns", "region");
  192. (0, proto_1.convertIfPresent)(eventTrigger, ep.eventTrigger, "channel", (c) => resolveChannelName(project, c, defaultRegion));
  193. (0, proto_1.convertIfPresent)(eventTrigger, ep.eventTrigger, "eventFilters", (filters) => {
  194. const copy = Object.assign({}, filters);
  195. if (copy["topic"] && !copy["topic"].startsWith("projects/")) {
  196. copy["topic"] = `projects/${project}/topics/${copy["topic"]}`;
  197. }
  198. return copy;
  199. });
  200. triggered = { eventTrigger };
  201. }
  202. else if (build.isHttpsTriggered(ep)) {
  203. triggered = { httpsTrigger: {} };
  204. (0, proto_1.copyIfPresent)(triggered.httpsTrigger, ep.httpsTrigger, "invoker");
  205. }
  206. else if (build.isCallableTriggered(ep)) {
  207. triggered = { callableTrigger: {} };
  208. }
  209. else if (build.isScheduleTriggered(ep)) {
  210. const st = {
  211. schedule: ep.scheduleTrigger.schedule || "",
  212. timeZone: (_a = ep.scheduleTrigger.timeZone) !== null && _a !== void 0 ? _a : null,
  213. };
  214. if (ep.scheduleTrigger.retryConfig) {
  215. st.retryConfig = {};
  216. (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "maxBackoffSeconds", "maxBackoffDuration", (duration) => (duration === null ? null : (0, proto_1.secondsFromDuration)(duration)));
  217. (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "minBackoffSeconds", "minBackoffDuration", (duration) => (duration === null ? null : (0, proto_1.secondsFromDuration)(duration)));
  218. (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "maxRetrySeconds", "maxRetryDuration", (duration) => (duration === null ? null : (0, proto_1.secondsFromDuration)(duration)));
  219. (0, proto_1.copyIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "retryCount", "minBackoffSeconds", "maxBackoffSeconds", "maxRetrySeconds", "maxDoublings");
  220. (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "minBackoffSeconds", "minBackoffDuration", (0, functional_1.nullsafeVisitor)(proto_1.secondsFromDuration));
  221. (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "maxBackoffSeconds", "maxBackoffDuration", (0, functional_1.nullsafeVisitor)(proto_1.secondsFromDuration));
  222. (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "maxRetrySeconds", "maxRetryDuration", (0, functional_1.nullsafeVisitor)(proto_1.secondsFromDuration));
  223. }
  224. else if (ep.scheduleTrigger.retryConfig === null) {
  225. st.retryConfig = null;
  226. }
  227. triggered = { scheduleTrigger: st };
  228. }
  229. else if (build.isTaskQueueTriggered(ep)) {
  230. const tq = {};
  231. if (ep.taskQueueTrigger.invoker) {
  232. tq.invoker = ep.taskQueueTrigger.invoker;
  233. }
  234. else if (ep.taskQueueTrigger.invoker === null) {
  235. tq.invoker = null;
  236. }
  237. if (ep.taskQueueTrigger.retryConfig) {
  238. tq.retryConfig = Object.assign({}, ep.taskQueueTrigger.retryConfig);
  239. }
  240. else if (ep.taskQueueTrigger.retryConfig === null) {
  241. tq.retryConfig = null;
  242. }
  243. if (ep.taskQueueTrigger.rateLimits) {
  244. tq.rateLimits = Object.assign({}, ep.taskQueueTrigger.rateLimits);
  245. }
  246. else if (ep.taskQueueTrigger.rateLimits === null) {
  247. tq.rateLimits = null;
  248. }
  249. triggered = { taskQueueTrigger: tq };
  250. }
  251. else if (ep.blockingTrigger) {
  252. triggered = { blockingTrigger: ep.blockingTrigger };
  253. }
  254. else {
  255. throw new error_1.FirebaseError(`Do not recognize trigger type for endpoint ${id}. Try upgrading ` +
  256. "firebase-tools with npm install -g firebase-tools@latest");
  257. }
  258. const parsed = Object.assign({ platform: ep.platform || "gcfv2", region: ep.region || [defaultRegion], project,
  259. runtime, entryPoint: ep.entryPoint }, triggered);
  260. if ("serviceAccountEmail" in ep) {
  261. parsed.serviceAccount = ep.serviceAccountEmail;
  262. }
  263. (0, proto_1.copyIfPresent)(parsed, ep, "omit", "availableMemoryMb", "cpu", "maxInstances", "minInstances", "concurrency", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables", "serviceAccount");
  264. (0, proto_1.convertIfPresent)(parsed, ep, "secretEnvironmentVariables", (senvs) => {
  265. if (!senvs) {
  266. return null;
  267. }
  268. return senvs.map(({ key, secret }) => {
  269. return { key, secret: secret || key, projectId: project };
  270. });
  271. });
  272. return parsed;
  273. }
  274. function resolveChannelName(projectId, channel, defaultRegion) {
  275. if (!channel.includes("/")) {
  276. const location = defaultRegion;
  277. const channelId = channel;
  278. return "projects/" + projectId + "/locations/" + location + "/channels/" + channelId;
  279. }
  280. const match = CHANNEL_NAME_REGEX.exec(channel);
  281. if (!(match === null || match === void 0 ? void 0 : match.groups)) {
  282. throw new error_1.FirebaseError("Invalid channel name format.");
  283. }
  284. const matchedProjectId = match.groups.project;
  285. const location = match.groups.location;
  286. const channelId = match.groups.channel;
  287. if (matchedProjectId) {
  288. return "projects/" + matchedProjectId + "/locations/" + location + "/channels/" + channelId;
  289. }
  290. else {
  291. return "projects/" + projectId + "/locations/" + location + "/channels/" + channelId;
  292. }
  293. }
  294. function isCEL(expr) {
  295. return typeof expr === "string" && expr.includes("{{") && expr.includes("}}");
  296. }