"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.jobFromEndpoint = exports.topicNameForEndpoint = exports.jobNameForEndpoint = exports.createOrReplaceJob = exports.getJob = exports.deleteJob = void 0; const _ = require("lodash"); const error_1 = require("../error"); const logger_1 = require("../logger"); const api_1 = require("../api"); const apiv2_1 = require("../apiv2"); const backend = require("../deploy/functions/backend"); const proto = require("./proto"); const checkIam_1 = require("../deploy/functions/checkIam"); const functional_1 = require("../functional"); const VERSION = "v1"; const DEFAULT_TIME_ZONE_V1 = "America/Los_Angeles"; const DEFAULT_TIME_ZONE_V2 = "UTC"; const apiClient = new apiv2_1.Client({ urlPrefix: api_1.cloudschedulerOrigin, apiVersion: VERSION }); function createJob(job) { const strippedName = job.name.substring(0, job.name.lastIndexOf("/")); const json = job.pubsubTarget ? Object.assign({ timeZone: DEFAULT_TIME_ZONE_V1 }, job) : Object.assign({ timeZone: DEFAULT_TIME_ZONE_V2 }, job); return apiClient.post(`/${strippedName}`, json); } function deleteJob(name) { return apiClient.delete(`/${name}`); } exports.deleteJob = deleteJob; function getJob(name) { return apiClient.get(`/${name}`, { resolveOnHTTPError: true, }); } exports.getJob = getJob; function updateJob(job) { let fieldMasks; let json; if (job.pubsubTarget) { fieldMasks = proto.fieldMasks(job, "pubsubTarget"); json = Object.assign({ timeZone: DEFAULT_TIME_ZONE_V1 }, job); } else { fieldMasks = proto.fieldMasks(job, "httpTarget"); json = Object.assign({ timeZone: DEFAULT_TIME_ZONE_V2 }, job); } return apiClient.patch(`/${job.name}`, json, { queryParams: { updateMask: fieldMasks.join(","), }, }); } async function createOrReplaceJob(job) { var _a, _b; const jobName = job.name.split("/").pop(); const existingJob = await getJob(job.name); if (existingJob.status === 404) { let newJob; try { newJob = await createJob(job); } catch (err) { if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 404) { throw new error_1.FirebaseError(`Cloud resource location is not set for this project but scheduled functions require it. ` + `Please see this documentation for more details: https://firebase.google.com/docs/projects/locations.`); } throw new error_1.FirebaseError(`Failed to create scheduler job ${job.name}: ${err.message}`); } logger_1.logger.debug(`created scheduler job ${jobName}`); return newJob; } if (!job.timeZone) { job.timeZone = job.pubsubTarget ? DEFAULT_TIME_ZONE_V1 : DEFAULT_TIME_ZONE_V2; } if (!needUpdate(existingJob.body, job)) { logger_1.logger.debug(`scheduler job ${jobName} is up to date, no changes required`); return; } const updatedJob = await updateJob(job); logger_1.logger.debug(`updated scheduler job ${jobName}`); return updatedJob; } exports.createOrReplaceJob = createOrReplaceJob; function needUpdate(existingJob, newJob) { if (!existingJob) { return true; } if (!newJob) { return true; } if (existingJob.schedule !== newJob.schedule) { return true; } if (existingJob.timeZone !== newJob.timeZone) { return true; } if (newJob.retryConfig) { if (!existingJob.retryConfig) { return true; } if (!_.isMatch(existingJob.retryConfig, newJob.retryConfig)) { return true; } } return false; } function jobNameForEndpoint(endpoint, location) { const id = backend.scheduleIdForFunction(endpoint); return `projects/${endpoint.project}/locations/${location}/jobs/${id}`; } exports.jobNameForEndpoint = jobNameForEndpoint; function topicNameForEndpoint(endpoint) { const id = backend.scheduleIdForFunction(endpoint); return `projects/${endpoint.project}/topics/${id}`; } exports.topicNameForEndpoint = topicNameForEndpoint; function jobFromEndpoint(endpoint, location, projectNumber) { const job = {}; job.name = jobNameForEndpoint(endpoint, location); if (endpoint.platform === "gcfv1") { job.timeZone = endpoint.scheduleTrigger.timeZone || DEFAULT_TIME_ZONE_V1; job.pubsubTarget = { topicName: topicNameForEndpoint(endpoint), attributes: { scheduled: "true", }, }; } else if (endpoint.platform === "gcfv2") { job.timeZone = endpoint.scheduleTrigger.timeZone || DEFAULT_TIME_ZONE_V2; job.httpTarget = { uri: endpoint.uri, httpMethod: "POST", oidcToken: { serviceAccountEmail: (0, checkIam_1.getDefaultComputeServiceAgent)(projectNumber), }, }; } else { (0, functional_1.assertExhaustive)(endpoint.platform); } if (!endpoint.scheduleTrigger.schedule) { throw new error_1.FirebaseError("Cannot create a scheduler job without a schedule:" + JSON.stringify(endpoint)); } job.schedule = endpoint.scheduleTrigger.schedule; if (endpoint.scheduleTrigger.retryConfig) { job.retryConfig = {}; proto.copyIfPresent(job.retryConfig, endpoint.scheduleTrigger.retryConfig, "maxDoublings", "retryCount"); proto.convertIfPresent(job.retryConfig, endpoint.scheduleTrigger.retryConfig, "maxBackoffDuration", "maxBackoffSeconds", (0, functional_1.nullsafeVisitor)(proto.durationFromSeconds)); proto.convertIfPresent(job.retryConfig, endpoint.scheduleTrigger.retryConfig, "minBackoffDuration", "minBackoffSeconds", (0, functional_1.nullsafeVisitor)(proto.durationFromSeconds)); proto.convertIfPresent(job.retryConfig, endpoint.scheduleTrigger.retryConfig, "maxRetryDuration", "maxRetrySeconds", (0, functional_1.nullsafeVisitor)(proto.durationFromSeconds)); if (!Object.keys(job.retryConfig).length) { delete job.retryConfig; } } return job; } exports.jobFromEndpoint = jobFromEndpoint;