UNPKG

@backstage/plugin-techdocs

Version:

The Backstage plugin that renders technical documentation for your components

166 lines (163 loc) • 5.89 kB
import { ResponseError, NotFoundError } from '@backstage/errors'; import { fetchEventSource } from '@microsoft/fetch-event-source'; class TechDocsClient { configApi; discoveryApi; fetchApi; constructor(options) { this.configApi = options.configApi; this.discoveryApi = options.discoveryApi; this.fetchApi = options.fetchApi; } async getCookie() { const apiOrigin = await this.getApiOrigin(); const requestUrl = `${apiOrigin}/cookie`; const response = await this.fetchApi.fetch(`${requestUrl}`, { credentials: "include" }); if (!response.ok) { throw await ResponseError.fromResponse(response); } return await response.json(); } async getApiOrigin() { return await this.discoveryApi.getBaseUrl("techdocs"); } /** * Retrieve TechDocs metadata. * * When docs are built, we generate a techdocs_metadata.json and store it along with the generated * static files. It includes necessary data about the docs site. This method requests techdocs-backend * which retrieves the TechDocs metadata. * * @param entityId - Object containing entity data like name, namespace, etc. */ async getTechDocsMetadata(entityId) { const { kind, namespace, name } = entityId; const apiOrigin = await this.getApiOrigin(); const requestUrl = `${apiOrigin}/metadata/techdocs/${namespace}/${kind}/${name}`; const request = await this.fetchApi.fetch(`${requestUrl}`); if (!request.ok) { throw await ResponseError.fromResponse(request); } return await request.json(); } /** * Retrieve metadata about an entity. * * This method requests techdocs-backend which uses the catalog APIs to respond with filtered * information required here. * * @param entityId - Object containing entity data like name, namespace, etc. */ async getEntityMetadata(entityId) { const { kind, namespace, name } = entityId; const apiOrigin = await this.getApiOrigin(); const requestUrl = `${apiOrigin}/metadata/entity/${namespace}/${kind}/${name}`; const request = await this.fetchApi.fetch(`${requestUrl}`); if (!request.ok) { throw await ResponseError.fromResponse(request); } return await request.json(); } } class TechDocsStorageClient { configApi; discoveryApi; fetchApi; constructor(options) { this.configApi = options.configApi; this.discoveryApi = options.discoveryApi; this.fetchApi = options.fetchApi; } async getApiOrigin() { return await this.discoveryApi.getBaseUrl("techdocs"); } async getStorageUrl() { return this.configApi.getOptionalString("techdocs.storageUrl") ?? `${await this.discoveryApi.getBaseUrl("techdocs")}/static/docs`; } async getBuilder() { return this.configApi.getOptionalString("techdocs.builder") || "local"; } /** * Fetch HTML content as text for an individual docs page in an entity's docs site. * * @param entityId - Object containing entity data like name, namespace, etc. * @param path - The unique path to an individual docs page e.g. overview/what-is-new * @returns HTML content of the docs page as string * @throws Throws error when the page is not found. */ async getEntityDocs(entityId, path) { const { kind, namespace, name } = entityId; const storageUrl = await this.getStorageUrl(); const url = `${storageUrl}/${namespace}/${kind}/${name}/${path}`; const request = await this.fetchApi.fetch( `${url.endsWith("/") ? url : `${url}/`}index.html` ); let errorMessage = ""; switch (request.status) { case 404: errorMessage = "Page not found. "; if (!path) { errorMessage += "This could be because there is no index.md file in the root of the docs directory of this repository."; } throw new NotFoundError(errorMessage); case 500: errorMessage = "Could not generate documentation or an error in the TechDocs backend. "; throw new Error(errorMessage); } return request.text(); } /** * Check if docs are on the latest version and trigger rebuild if not * * @param entityId - Object containing entity data like name, namespace, etc. * @param logHandler - Callback to receive log messages from the build process * @returns Whether documents are currently synchronized to newest version * @throws Throws error on error from sync endpoint in TechDocs Backend */ async syncEntityDocs(entityId, logHandler = () => { }) { const { kind, namespace, name } = entityId; const apiOrigin = await this.getApiOrigin(); const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`; return new Promise((resolve, reject) => { const ctrl = new AbortController(); fetchEventSource(url, { fetch: this.fetchApi.fetch, signal: ctrl.signal, onmessage(e) { if (e.event === "log") { if (e.data) { logHandler(JSON.parse(e.data)); } } else if (e.event === "finish") { let updated = false; if (e.data) { ({ updated } = JSON.parse(e.data)); } resolve(updated ? "updated" : "cached"); } else if (e.event === "error") { reject(new Error(e.data)); } }, onerror(err) { ctrl.abort(); reject(err); throw err; } }); }); } async getBaseUrl(oldBaseUrl, entityId, path) { const { kind, namespace, name } = entityId; const apiOrigin = await this.getApiOrigin(); const newBaseUrl = `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`; return new URL( oldBaseUrl, newBaseUrl.endsWith("/") ? newBaseUrl : `${newBaseUrl}/` ).toString(); } } export { TechDocsClient, TechDocsStorageClient }; //# sourceMappingURL=client.esm.js.map