@backstage/plugin-techdocs
Version:
The Backstage plugin that renders technical documentation for your components
166 lines (163 loc) • 5.89 kB
JavaScript
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