diff --git a/.changeset/green-otters-wait.md b/.changeset/green-otters-wait.md new file mode 100644 index 0000000000..4f9b02351e --- /dev/null +++ b/.changeset/green-otters-wait.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog-unprocessed-entities': patch +--- + +Export some types and API items. This allows people to call the API from different places with the ApiRef, as well +as completely customize the client if required. Check the [README.md](../plugins/catalog-unprocessed-entities/README.md) to +note what needs to be added in order to use the new `catalogUnprocessedEntitiesApiRef` exported function. diff --git a/plugins/catalog-unprocessed-entities/README.md b/plugins/catalog-unprocessed-entities/README.md index c0dd996016..699629e0b2 100644 --- a/plugins/catalog-unprocessed-entities/README.md +++ b/plugins/catalog-unprocessed-entities/README.md @@ -44,6 +44,39 @@ import { CatalogUnprocessedEntitiesPage } from '@backstage/plugin-catalog-unproc />; ``` +## Customization + +If you want to use the provided endpoints in a different way, you can use the ApiRef doing the following: + +```typescript +import { catalogUnprocessedEntitiesApiRef } from '@backstage/plugin-catalog-unprocessed-entities'; +import { useApi } from '@backstage/core-plugin-api'; + +const catalogUnprocessedEntitiesApi = useApi(catalogUnprocessedEntitiesApiRef); +``` + +Note that if you are not rendering the `CatalogUnprocessedEntitiesPage` in the `App.tsx` tree, you will need to export the `catalogUnproccessedEntitiesPlugin` from your `plugins.ts` file to setup the plugin otherwise you will receive an error like `No implementation available for apiRef{plugin.catalog-unprocessed-entities.service}` + +```typescript +// In packages/app/src/plugins.ts +... +export { catalogUnprocessedEntitiesPlugin } from '@backstage/plugin-catalog-unprocessed-entities'; +``` + +If you don't have a `plugins.ts` file, you can create it with the path `packages/app/src/plugins.ts` and then import it into your `App.tsx`: + +```diff ++ import * as plugins from './plugins'; + +const app = createApp({ + apis, ++ plugins: Object.values(plugins), + bindRoutes({ bind }) { + /* ... */ + }, +}); +``` + ## Getting started Your plugin has been added to the example app in this repository, diff --git a/plugins/catalog-unprocessed-entities/api-report.md b/plugins/catalog-unprocessed-entities/api-report.md index 940bc2b3a5..0ccf9716f2 100644 --- a/plugins/catalog-unprocessed-entities/api-report.md +++ b/plugins/catalog-unprocessed-entities/api-report.md @@ -5,9 +5,25 @@ ```ts /// +import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { Entity } from '@backstage/catalog-model'; import { RouteRef } from '@backstage/core-plugin-api'; +// @public +export interface CatalogUnprocessedEntitiesApi { + failed(): Promise; + pending(): Promise; +} + +// @public +export const catalogUnprocessedEntitiesApiRef: ApiRef; + +// @public +export type CatalogUnprocessedEntitiesApiResponse = { + entities: UnprocessedEntity[]; +}; + // @public export const CatalogUnprocessedEntitiesPage: () => JSX.Element; @@ -23,5 +39,37 @@ export const catalogUnprocessedEntitiesPlugin: BackstagePlugin< // @public (undocumented) export const UnprocessedEntitiesContent: () => JSX.Element; +// @public +export type UnprocessedEntity = { + entity_id: string; + entity_ref: string; + unprocessed_entity: Entity; + unprocessed_hash?: string; + processed_entity?: Entity; + result_hash?: string; + cache?: UnprocessedEntityCache; + next_update_at: string | Date; + last_discovery_at: string | Date; + errors?: UnprocessedEntityError[]; + location_key?: string; +}; + +// @public +export type UnprocessedEntityCache = { + ttl: number; + cache: object; +}; + +// @public +export type UnprocessedEntityError = { + name: string; + message: string; + cause: { + name: string; + message: string; + stack: string; + }; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-unprocessed-entities/src/api/index.ts b/plugins/catalog-unprocessed-entities/src/api/index.ts index 5be81c8331..92dde93a93 100644 --- a/plugins/catalog-unprocessed-entities/src/api/index.ts +++ b/plugins/catalog-unprocessed-entities/src/api/index.ts @@ -32,20 +32,43 @@ export const catalogUnprocessedEntitiesApiRef = }); /** - * API client for the Catalog Unprocessed Entities plugin + * Response expected by the {@link CatalogUnprocessedEntitiesApi} * * @public */ -export class CatalogUnprocessedEntitiesApi { - url: string = ''; +export type CatalogUnprocessedEntitiesApiResponse = { + entities: UnprocessedEntity[]; +}; +/** + * Interface for the CatalogUnprocessedEntitiesApi. + * + * @public + */ +export interface CatalogUnprocessedEntitiesApi { + /** + * Returns a list of entities with state 'pending' + */ + pending(): Promise; + /** + * Returns a list of entities with state 'failed' + */ + failed(): Promise; +} + +/** + * Default API implementation for the Catalog Unprocessed Entities plugin + * + * @public + */ +export class CatalogUnprocessedEntitiesClient + implements CatalogUnprocessedEntitiesApi +{ constructor(public discovery: DiscoveryApi, public fetchApi: FetchApi) {} private async fetch(path: string, init?: RequestInit): Promise { - if (!this.url) { - this.url = await this.discovery.getBaseUrl('catalog'); - } - const resp = await this.fetchApi.fetch(`${this.url}/${path}`, init); + const url = await this.discovery.getBaseUrl('catalog'); + const resp = await this.fetchApi.fetch(`${url}/${path}`, init); if (!resp.ok) { throw await ResponseError.fromResponse(resp); } @@ -53,11 +76,11 @@ export class CatalogUnprocessedEntitiesApi { return await resp.json(); } - async pending(): Promise<{ entities: UnprocessedEntity[] }> { + async pending(): Promise { return await this.fetch('entities/unprocessed/pending'); } - async failed(): Promise<{ entities: UnprocessedEntity[] }> { + async failed(): Promise { return await this.fetch('entities/unprocessed/failed'); } } diff --git a/plugins/catalog-unprocessed-entities/src/index.ts b/plugins/catalog-unprocessed-entities/src/index.ts index 11ed24113f..4fcc9a8fcd 100644 --- a/plugins/catalog-unprocessed-entities/src/index.ts +++ b/plugins/catalog-unprocessed-entities/src/index.ts @@ -18,3 +18,13 @@ export { CatalogUnprocessedEntitiesPage, } from './plugin'; export { UnprocessedEntitiesContent } from './components/UnprocessedEntities'; +export { + type CatalogUnprocessedEntitiesApiResponse, + type CatalogUnprocessedEntitiesApi, + catalogUnprocessedEntitiesApiRef, +} from './api'; +export type { + UnprocessedEntity, + UnprocessedEntityCache, + UnprocessedEntityError, +} from './types'; diff --git a/plugins/catalog-unprocessed-entities/src/plugin.ts b/plugins/catalog-unprocessed-entities/src/plugin.ts index 14598c825b..baf7278454 100644 --- a/plugins/catalog-unprocessed-entities/src/plugin.ts +++ b/plugins/catalog-unprocessed-entities/src/plugin.ts @@ -23,7 +23,7 @@ import { import { rootRouteRef } from './routes'; import { - CatalogUnprocessedEntitiesApi, + CatalogUnprocessedEntitiesClient, catalogUnprocessedEntitiesApiRef, } from './api'; @@ -42,7 +42,7 @@ export const catalogUnprocessedEntitiesPlugin = createPlugin({ api: catalogUnprocessedEntitiesApiRef, deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef }, factory: ({ discoveryApi, fetchApi }) => - new CatalogUnprocessedEntitiesApi(discoveryApi, fetchApi), + new CatalogUnprocessedEntitiesClient(discoveryApi, fetchApi), }), ], }); diff --git a/plugins/catalog-unprocessed-entities/src/types.ts b/plugins/catalog-unprocessed-entities/src/types.ts index 490027b57a..65f21970f5 100644 --- a/plugins/catalog-unprocessed-entities/src/types.ts +++ b/plugins/catalog-unprocessed-entities/src/types.ts @@ -15,6 +15,10 @@ */ import { Entity } from '@backstage/catalog-model'; +/** + * Unprocessed entity data stored in the database. + * @public + */ export type UnprocessedEntity = { entity_id: string; entity_ref: string; @@ -29,11 +33,19 @@ export type UnprocessedEntity = { location_key?: string; }; +/** + * Unprocessed entity cache stored in the database. + * @public + */ export type UnprocessedEntityCache = { ttl: number; cache: object; }; +/** + * Unprocessed entity error information stored in the database. + * @public + */ export type UnprocessedEntityError = { name: string; message: string;