UNPKG

@backstage/backend-defaults

Version:

Backend defaults used by Backstage backend apps

225 lines (219 loc) • 8.48 kB
'use strict'; var base64Stream = require('base64-stream'); var fetch = require('node-fetch'); var stream = require('stream'); var integration = require('@backstage/integration'); var errors = require('@backstage/errors'); var minimatch = require('minimatch'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch); class GerritUrlReader { static factory = ({ config, treeResponseFactory }) => { const integrations = integration.ScmIntegrations.fromConfig(config); if (!integrations.gerrit) { return []; } return integrations.gerrit.list().map((integration) => { const reader = new GerritUrlReader(integration, { treeResponseFactory }); const predicate = (url) => { const gitilesUrl = new URL(integration.config.gitilesBaseUrl); return url.host === gitilesUrl.host; }; return { reader, predicate }; }); }; integration; deps; constructor(integration, deps) { this.integration = integration; this.deps = deps; } async read(url) { const response = await this.readUrl(url); return response.buffer(); } async readUrl(url, options) { const apiUrl = integration.getGerritFileContentsApiUrl(this.integration.config, url); let response; try { response = await fetch__default.default(apiUrl, { method: "GET", ...integration.getGerritRequestOptions(this.integration.config), // TODO(freben): The signal cast is there because pre-3.x versions of // node-fetch have a very slightly deviating AbortSignal type signature. // The difference does not affect us in practice however. The cast can // be removed after we support ESM for CLI dependencies and migrate to // version 3 of node-fetch. // https://github.com/backstage/backstage/issues/8242 signal: options?.signal }); } catch (e) { throw new Error(`Unable to read gerrit file ${url}, ${e}`); } if (response.ok) { let responseBody; return { buffer: async () => { if (responseBody === void 0) { responseBody = await response.text(); } return Buffer.from(responseBody, "base64"); }, stream: () => { const readable = stream.Readable.from(response.body); return readable.pipe(new base64Stream.Base64Decode()); } }; } if (response.status === 404) { throw new errors.NotFoundError(`File ${url} not found.`); } throw new Error( `${url} could not be read as ${apiUrl}, ${response.status} ${response.statusText}` ); } async readTree(url, options) { const urlRevision = await this.getRevisionForUrl(url, options); return this.readTreeFromGitiles(url, urlRevision, options); } async search(url, options) { const { path } = integration.parseGitilesUrlRef(this.integration.config, url); if (!path.match(/[*?]/)) { try { const data = await this.readUrl(url, options); return { files: [ { url, content: data.buffer, lastModifiedAt: data.lastModifiedAt } ], etag: data.etag ?? "" }; } catch (error) { errors.assertError(error); if (error.name === "NotFoundError") { return { files: [], etag: "" }; } throw error; } } const urlRevision = await this.getRevisionForUrl(url, options); const files = await this.searchFilesFromGitiles(url, options); return { files, etag: urlRevision }; } toString() { const { host, password } = this.integration.config; return `gerrit{host=${host},authed=${Boolean(password)}}`; } async readTreeFromGitiles(url, revision, options) { const archiveUrl = integration.buildGerritGitilesArchiveUrlFromLocation( this.integration.config, url ); const archiveResponse = await fetch__default.default(archiveUrl, { ...integration.getGerritRequestOptions(this.integration.config), // TODO(freben): The signal cast is there because pre-3.x versions of // node-fetch have a very slightly deviating AbortSignal type signature. // The difference does not affect us in practice however. The cast can // be removed after we support ESM for CLI dependencies and migrate to // version 3 of node-fetch. // https://github.com/backstage/backstage/issues/8242 signal: options?.signal }); if (archiveResponse.status === 404) { throw new errors.NotFoundError(`Not found: ${archiveUrl}`); } if (!archiveResponse.ok) { throw new Error( `${url} could not be read as ${archiveUrl}, ${archiveResponse.status} ${archiveResponse.statusText}` ); } return await this.deps.treeResponseFactory.fromTarArchive({ stream: archiveResponse.body, etag: revision, filter: options?.filter, stripFirstDirectory: false }); } async getRevisionForUrl(url, options) { const { ref, refType } = integration.parseGitilesUrlRef(this.integration.config, url); if (refType === "sha") { if (options?.etag === ref) { throw new errors.NotModifiedError(); } return ref; } const apiUrl = integration.getGerritBranchApiUrl(this.integration.config, url); let response; try { response = await fetch__default.default(apiUrl, { method: "GET", ...integration.getGerritRequestOptions(this.integration.config) }); } catch (e) { throw new Error(`Unable to read branch state ${url}, ${e}`); } if (!response.ok) { throw await errors.ResponseError.fromResponse(response); } const branchInfo = await integration.parseGerritJsonResponse(response); if (options?.etag === branchInfo.revision) { throw new errors.NotModifiedError(); } return branchInfo.revision; } async searchFilesFromGitiles(url, options) { const { path, basePath } = integration.parseGitilesUrlRef(this.integration.config, url); const treeUrl = `${basePath}/?format=JSON&recursive`.replace( this.integration.config.gitilesBaseUrl, integration.getGitilesAuthenticationUrl(this.integration.config) ); const treeResponse = await fetch__default.default(treeUrl, { ...integration.getGerritRequestOptions(this.integration.config), // TODO(freben): The signal cast is there because pre-3.x versions of // node-fetch have a very slightly deviating AbortSignal type signature. // The difference does not affect us in practice however. The cast can // be removed after we support ESM for CLI dependencies and migrate to // version 3 of node-fetch. // https://github.com/backstage/backstage/issues/8242 signal: options?.signal }); if (!treeResponse.ok) { throw await errors.ResponseError.fromResponse(treeResponse); } const res = await integration.parseGerritJsonResponse(treeResponse); const matcher = new minimatch.Minimatch(decodeURIComponent(path).replace(/^\/+/, "")); const matching = res.entries.filter( (item) => item.type === "blob" && item.name && matcher.match(item.name) ); return matching.map((item) => ({ url: `${basePath}/${item.name}`, content: async () => { const apiUrl = integration.getGerritFileContentsApiUrl( this.integration.config, `${basePath}/${item.name}` ); const response = await fetch__default.default(apiUrl, { method: "GET", ...integration.getGerritRequestOptions(this.integration.config), // TODO(freben): The signal cast is there because pre-3.x versions of // node-fetch have a very slightly deviating AbortSignal type signature. // The difference does not affect us in practice however. The cast can // be removed after we support ESM for CLI dependencies and migrate to // version 3 of node-fetch. // https://github.com/backstage/backstage/issues/8242 signal: options?.signal }); const responseBody = await response.text(); return Buffer.from(responseBody, "base64"); } })); } } exports.GerritUrlReader = GerritUrlReader; //# sourceMappingURL=GerritUrlReader.cjs.js.map