UNPKG

firebase-tools

Version:
170 lines (169 loc) 7.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.squashGraphQL = exports.readGQLFiles = exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = exports.load = exports.loadAll = exports.pickServices = exports.pickOneService = void 0; const path = require("path"); const fs = require("fs-extra"); const clc = require("colorette"); const glob_1 = require("glob"); const error_1 = require("../error"); const types_1 = require("./types"); const utils_1 = require("../utils"); const experiments = require("../experiments"); async function pickOneService(projectId, config, service, location) { const services = await pickServices(projectId, config, service, location); if (services.length > 1) { const serviceIds = services.map((i) => `${i.dataConnectYaml.location}:${i.dataConnectYaml.serviceId}`); throw new error_1.FirebaseError(`Multiple services matched. Please specify a service and location. Matched services: ${serviceIds.join(", ")}`); } return services[0]; } exports.pickOneService = pickOneService; async function pickServices(projectId, config, serviceId, location) { const serviceInfos = await loadAll(projectId, config); if (serviceInfos.length === 0) { throw new error_1.FirebaseError("No Data Connect services found in firebase.json." + `\nYou can run ${clc.bold("firebase init dataconnect")} to add a Data Connect service.`); } const matchingServices = serviceInfos.filter((i) => (!serviceId || i.dataConnectYaml.serviceId === serviceId) && (!location || i.dataConnectYaml.location === location)); if (matchingServices.length === 0) { const serviceIds = serviceInfos.map((i) => `${i.dataConnectYaml.location}:${i.dataConnectYaml.serviceId}`); throw new error_1.FirebaseError(`No service matched service in firebase.json. Available services: ${serviceIds.join(", ")}`); } return matchingServices; } exports.pickServices = pickServices; async function loadAll(projectId, config) { const serviceCfgs = readFirebaseJson(config); return await Promise.all(serviceCfgs.map((c) => load(projectId, config, c.source))); } exports.loadAll = loadAll; async function load(projectId, config, sourceDirectory) { const resolvedDir = config.path(sourceDirectory); const dataConnectYaml = await readDataConnectYaml(resolvedDir); const serviceName = `projects/${projectId}/locations/${dataConnectYaml.location}/services/${dataConnectYaml.serviceId}`; const schemaYamls = dataConnectYaml.schema ? [dataConnectYaml.schema] : dataConnectYaml.schemas; const schemas = await Promise.all(schemaYamls.map(async (yaml) => { const schemaDir = path.join(resolvedDir, yaml.source); const schemaGQLs = await readGQLFiles(schemaDir); return { name: `${serviceName}/schemas/${yaml.id || types_1.MAIN_SCHEMA_ID}`, datasources: [(0, types_1.toDatasource)(projectId, dataConnectYaml.location, yaml.datasource)], source: { files: schemaGQLs, }, }; })); const connectorInfo = await Promise.all(dataConnectYaml.connectorDirs.map(async (dir) => { const connectorDir = path.join(resolvedDir, dir); const connectorYaml = await readConnectorYaml(connectorDir); const connectorGqls = await readGQLFiles(connectorDir); return { directory: connectorDir, connectorYaml, connector: { name: `${serviceName}/connectors/${connectorYaml.connectorId}`, source: { files: connectorGqls, }, }, }; })); return { serviceName, sourceDirectory: resolvedDir, schemas: schemas, dataConnectYaml, connectorInfo, }; } exports.load = load; function readFirebaseJson(config) { if (!(config === null || config === void 0 ? void 0 : config.has("dataconnect"))) { return []; } const validator = (cfg) => { if (!cfg["source"]) { throw new error_1.FirebaseError("Invalid firebase.json: DataConnect requires `source`"); } return { source: cfg["source"], }; }; const configs = config.get("dataconnect"); if (typeof configs === "object" && !Array.isArray(configs)) { return [validator(configs)]; } else if (Array.isArray(configs)) { return configs.map(validator); } else { throw new error_1.FirebaseError("Invalid firebase.json: dataconnect should be of the form { source: string }"); } } exports.readFirebaseJson = readFirebaseJson; async function readDataConnectYaml(sourceDirectory) { const file = await (0, utils_1.readFileFromDirectory)(sourceDirectory, "dataconnect.yaml"); const dataconnectYaml = await (0, utils_1.wrappedSafeLoad)(file.source); return validateDataConnectYaml(dataconnectYaml); } exports.readDataConnectYaml = readDataConnectYaml; function validateDataConnectYaml(unvalidated) { if (!unvalidated["location"]) { throw new error_1.FirebaseError("Missing required field 'location' in dataconnect.yaml"); } if (!experiments.isEnabled("fdcwebhooks") && unvalidated["schemas"]) { throw new error_1.FirebaseError("Unsupported field 'schemas' in dataconnect.yaml"); } if (!unvalidated["schema"] && !unvalidated["schemas"]) { throw new error_1.FirebaseError("Either 'schema' or 'schemas' is required in dataconnect.yaml"); } return unvalidated; } async function readConnectorYaml(sourceDirectory) { const file = await (0, utils_1.readFileFromDirectory)(sourceDirectory, "connector.yaml"); const connectorYaml = await (0, utils_1.wrappedSafeLoad)(file.source); return validateConnectorYaml(connectorYaml); } exports.readConnectorYaml = readConnectorYaml; function validateConnectorYaml(unvalidated) { return unvalidated; } async function readGQLFiles(sourceDir) { if (!fs.existsSync(sourceDir)) { return []; } const files = await (0, glob_1.glob)("**/*.{gql,graphql}", { cwd: sourceDir, absolute: true, nodir: true }); return files.map((f) => toFile(sourceDir, f)); } exports.readGQLFiles = readGQLFiles; function toFile(sourceDir, fullPath) { const relPath = path.relative(sourceDir, fullPath); if (!fs.existsSync(fullPath)) { throw new error_1.FirebaseError(`file ${fullPath} not found`); } const content = fs.readFileSync(fullPath).toString(); return { path: relPath, content, }; } function squashGraphQL(source) { if (!source.files || !source.files.length) { return ""; } if (source.files.length === 1) { return source.files[0].content; } let query = ""; for (const f of source.files) { if (!f.content || !/\S/.test(f.content)) { continue; } query += `### Begin file ${f.path}\n`; query += f.content; query += `### End file ${f.path}\n`; } return query; } exports.squashGraphQL = squashGraphQL;