From df4d64609ec5bd902509368b274999fa3ce063b3 Mon Sep 17 00:00:00 2001 From: ivangonzalezacuna Date: Wed, 5 Nov 2025 13:32:41 +0100 Subject: [PATCH] [catalog-unprocessed-entities] Move types and clients from frontend to common plugin Moved some types, as well as the API and client definitions to the common package. The original types, client and interface are now re-exported from the common package instead. This allows not only frontend plugins, but also backend ones, to use this client if needed. Until now, and since the types and all definitions were stored in the frontend plugin, the only way to implement such feature was to copy the code in the project. It would make much more sense to make this exportable from the plugin itself and let people use this plugin if needed. I made this change a patch, since all types have been initially deprecated and a mention to the new location has been added. Signed-off-by: ivangonzalezacuna --- .changeset/rotten-melons-sleep.md | 15 +++ .../package.json | 2 + .../report.api.md | 85 ++++++++++++ .../src/api/api.ts | 124 ++++++++++++++++++ .../src/api/index.ts | 16 +++ .../src/index.ts | 2 + .../src/types.ts | 66 ++++++++++ .../catalog-unprocessed-entities/package.json | 2 +- .../report.api.md | 57 +++----- .../src/api/index.ts | 72 ++-------- .../catalog-unprocessed-entities/src/types.ts | 38 ++---- yarn.lock | 4 +- 12 files changed, 355 insertions(+), 128 deletions(-) create mode 100644 .changeset/rotten-melons-sleep.md create mode 100644 plugins/catalog-unprocessed-entities-common/src/api/api.ts create mode 100644 plugins/catalog-unprocessed-entities-common/src/api/index.ts create mode 100644 plugins/catalog-unprocessed-entities-common/src/types.ts diff --git a/.changeset/rotten-melons-sleep.md b/.changeset/rotten-melons-sleep.md new file mode 100644 index 0000000000..c12e0a9669 --- /dev/null +++ b/.changeset/rotten-melons-sleep.md @@ -0,0 +1,15 @@ +--- +'@backstage/plugin-catalog-unprocessed-entities-common': patch +'@backstage/plugin-catalog-unprocessed-entities': patch +--- + +Moved types, API and client to the common package, allowing both frontend and +backend plugins to use the `CatalogUnprocessedEntitiesClient`. + +The following types, clients and interfaces have been deprecated and should be +imported from the `@backstage/plugin-catalog-unprocessed-entities-common` instead: +`CatalogUnprocessedEntitiesApi`, `CatalogUnprocessedEntitiesApiResponse`, `UnprocessedEntity`, +`UnprocessedEntityCache`, `UnprocessedEntityError`, `CatalogUnprocessedEntitiesClient`. + +All those types, clients and interfaces are re-exported temporarily in the +`@backstage/plugin-catalog-unprocessed-entities` package until cleaned up. diff --git a/plugins/catalog-unprocessed-entities-common/package.json b/plugins/catalog-unprocessed-entities-common/package.json index 49ead8da1b..68e1a23dd0 100644 --- a/plugins/catalog-unprocessed-entities-common/package.json +++ b/plugins/catalog-unprocessed-entities-common/package.json @@ -37,6 +37,8 @@ "test": "backstage-cli package test" }, "dependencies": { + "@backstage/catalog-model": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/plugin-permission-common": "workspace:^" }, "devDependencies": { diff --git a/plugins/catalog-unprocessed-entities-common/report.api.md b/plugins/catalog-unprocessed-entities-common/report.api.md index 03b316eebb..e373a3f45b 100644 --- a/plugins/catalog-unprocessed-entities-common/report.api.md +++ b/plugins/catalog-unprocessed-entities-common/report.api.md @@ -4,6 +4,53 @@ ```ts import { BasicPermission } from '@backstage/plugin-permission-common'; +import { Entity } from '@backstage/catalog-model'; + +// @public +export interface CatalogUnprocessedEntitiesApi { + delete( + entityId: string, + options?: UnprocessedEntitiesRequestOptions, + ): Promise; + failed( + options?: UnprocessedEntitiesRequestOptions, + ): Promise; + pending( + options?: UnprocessedEntitiesRequestOptions, + ): Promise; +} + +// @public +export type CatalogUnprocessedEntitiesApiResponse = { + entities: UnprocessedEntity[]; +}; + +// @public +export class CatalogUnprocessedEntitiesClient + implements CatalogUnprocessedEntitiesApi +{ + constructor( + discovery: { + getBaseUrl(pluginId: string): Promise; + }, + fetchApi?: { + fetch: typeof fetch; + }, + ); + // (undocumented) + delete( + entityId: string, + options?: UnprocessedEntitiesRequestOptions, + ): Promise; + // (undocumented) + failed( + options?: UnprocessedEntitiesRequestOptions, + ): Promise; + // (undocumented) + pending( + options?: UnprocessedEntitiesRequestOptions, + ): Promise; +} // @public export const unprocessedEntitiesDeletePermission: BasicPermission; @@ -12,4 +59,42 @@ export const unprocessedEntitiesDeletePermission: BasicPermission; export const unprocessedEntitiesPermissions: { unprocessedEntitiesDeletePermission: BasicPermission; }; + +// @public +export interface UnprocessedEntitiesRequestOptions { + // (undocumented) + token?: string; +} + +// @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; + }; +}; ``` diff --git a/plugins/catalog-unprocessed-entities-common/src/api/api.ts b/plugins/catalog-unprocessed-entities-common/src/api/api.ts new file mode 100644 index 0000000000..02b230aaec --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/src/api/api.ts @@ -0,0 +1,124 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CatalogUnprocessedEntitiesApiResponse } from '../types'; +import { ResponseError } from '@backstage/errors'; + +/** + * Options you can pass into a catalog request for additional information. + * + * @public + */ +export interface UnprocessedEntitiesRequestOptions { + token?: string; +} + +/** + * Interface for the CatalogUnprocessedEntitiesApi. + * + * @public + */ +export interface CatalogUnprocessedEntitiesApi { + /** + * Returns a list of entities with state 'pending' + * + * @param options - Additional options + */ + pending( + options?: UnprocessedEntitiesRequestOptions, + ): Promise; + /** + * Returns a list of entities with state 'failed' + * + * @param options - Additional options + */ + failed( + options?: UnprocessedEntitiesRequestOptions, + ): Promise; + /** + * Deletes an entity from the refresh_state table + * + * @param entityId - The ID of the entity to delete + * @param options - Additional options + */ + delete( + entityId: string, + options?: UnprocessedEntitiesRequestOptions, + ): Promise; +} + +/** + * Default API implementation for the Catalog Unprocessed Entities plugin + * + * @public + */ +export class CatalogUnprocessedEntitiesClient + implements CatalogUnprocessedEntitiesApi +{ + private readonly discovery: { getBaseUrl(pluginId: string): Promise }; + private readonly fetchApi: { fetch: typeof fetch }; + + constructor( + discovery: { getBaseUrl(pluginId: string): Promise }, + fetchApi?: { fetch: typeof fetch }, + ) { + this.discovery = discovery; + this.fetchApi = fetchApi ?? { fetch }; + } + + private async fetch( + method: string, + path: string, + options?: UnprocessedEntitiesRequestOptions, + ): Promise { + const url = await this.discovery.getBaseUrl('catalog'); + const resp = await this.fetchApi.fetch(`${url}/${path}`, { + method, + headers: { + ...(options?.token ? { Authorization: `Bearer ${options.token}` } : {}), + }, + }); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + + return resp.status === 204 ? (resp as T) : await resp.json(); + } + + async pending( + options?: UnprocessedEntitiesRequestOptions, + ): Promise { + return await this.fetch('GET', 'entities/unprocessed/pending', options); + } + + async failed( + options?: UnprocessedEntitiesRequestOptions, + ): Promise { + return await this.fetch('GET', 'entities/unprocessed/failed', options); + } + + async delete( + entityId: string, + options?: UnprocessedEntitiesRequestOptions, + ): Promise { + await this.fetch( + 'DELETE', + `entities/unprocessed/delete/${entityId}`, + options, + ); + } +} diff --git a/plugins/catalog-unprocessed-entities-common/src/api/index.ts b/plugins/catalog-unprocessed-entities-common/src/api/index.ts new file mode 100644 index 0000000000..5b8bf17e1d --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/src/api/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './api'; diff --git a/plugins/catalog-unprocessed-entities-common/src/index.ts b/plugins/catalog-unprocessed-entities-common/src/index.ts index cd5a39c731..d11cb4fdb5 100644 --- a/plugins/catalog-unprocessed-entities-common/src/index.ts +++ b/plugins/catalog-unprocessed-entities-common/src/index.ts @@ -20,4 +20,6 @@ * @packageDocumentation */ +export * from './api'; export * from './permissions'; +export * from './types'; diff --git a/plugins/catalog-unprocessed-entities-common/src/types.ts b/plugins/catalog-unprocessed-entities-common/src/types.ts new file mode 100644 index 0000000000..d7af485e83 --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/src/types.ts @@ -0,0 +1,66 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity } from '@backstage/catalog-model'; + +/** + * Unprocessed entity data stored in the database. + * @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; // remove? + errors?: UnprocessedEntityError[]; + 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; + cause: { + name: string; + message: string; + stack: string; + }; +}; + +/** + * Response expected by the {@link CatalogUnprocessedEntitiesApi} + * + * @public + */ +export type CatalogUnprocessedEntitiesApiResponse = { + entities: UnprocessedEntity[]; +}; diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index a6e960367e..82ea2238bc 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -50,11 +50,11 @@ "test": "backstage-cli package test" }, "dependencies": { - "@backstage/catalog-model": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.60", diff --git a/plugins/catalog-unprocessed-entities/report.api.md b/plugins/catalog-unprocessed-entities/report.api.md index adaf7af643..28b55b9a6d 100644 --- a/plugins/catalog-unprocessed-entities/report.api.md +++ b/plugins/catalog-unprocessed-entities/report.api.md @@ -5,24 +5,24 @@ ```ts import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { Entity } from '@backstage/catalog-model'; +import { CatalogUnprocessedEntitiesApi as CatalogUnprocessedEntitiesApi_2 } from '@backstage/plugin-catalog-unprocessed-entities-common'; +import { CatalogUnprocessedEntitiesApiResponse as CatalogUnprocessedEntitiesApiResponse_2 } from '@backstage/plugin-catalog-unprocessed-entities-common'; import { JSX as JSX_2 } from 'react/jsx-runtime'; import { RouteRef } from '@backstage/core-plugin-api'; +import type { UnprocessedEntity as UnprocessedEntity_2 } from '@backstage/plugin-catalog-unprocessed-entities-common'; +import type { UnprocessedEntityCache as UnprocessedEntityCache_2 } from '@backstage/plugin-catalog-unprocessed-entities-common'; +import type { UnprocessedEntityError as UnprocessedEntityError_2 } from '@backstage/plugin-catalog-unprocessed-entities-common'; -// @public -export interface CatalogUnprocessedEntitiesApi { - delete(entityId: string): Promise; - failed(): Promise; - pending(): Promise; -} +// @public @deprecated +export interface CatalogUnprocessedEntitiesApi + extends CatalogUnprocessedEntitiesApi_2 {} // @public export const catalogUnprocessedEntitiesApiRef: ApiRef; -// @public -export type CatalogUnprocessedEntitiesApiResponse = { - entities: UnprocessedEntity[]; -}; +// @public @deprecated +export type CatalogUnprocessedEntitiesApiResponse = + CatalogUnprocessedEntitiesApiResponse_2; // @public export const CatalogUnprocessedEntitiesPage: () => JSX_2.Element; @@ -38,37 +38,14 @@ export const catalogUnprocessedEntitiesPlugin: BackstagePlugin< // @public (undocumented) export const UnprocessedEntitiesContent: () => JSX_2.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 @deprecated +export type UnprocessedEntity = UnprocessedEntity_2; -// @public -export type UnprocessedEntityCache = { - ttl: number; - cache: object; -}; +// @public @deprecated +export type UnprocessedEntityCache = UnprocessedEntityCache_2; -// @public -export type UnprocessedEntityError = { - name: string; - message: string; - cause: { - name: string; - message: string; - stack: string; - }; -}; +// @public @deprecated +export type UnprocessedEntityError = UnprocessedEntityError_2; // (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 67daa81319..cec9bd29bd 100644 --- a/plugins/catalog-unprocessed-entities/src/api/index.ts +++ b/plugins/catalog-unprocessed-entities/src/api/index.ts @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createApiRef } from '@backstage/core-plugin-api'; import { - DiscoveryApi, - createApiRef, - FetchApi, -} from '@backstage/core-plugin-api'; -import { ResponseError } from '@backstage/errors'; -import { UnprocessedEntity } from '../types'; + CatalogUnprocessedEntitiesApiResponse as CommonCatalogUnprocessedEntitiesApiResponse, + CatalogUnprocessedEntitiesApi as CommonCatalogUnprocessedEntitiesApi, + CatalogUnprocessedEntitiesClient as CommonCatalogUnprocessedEntitiesClient, +} from '@backstage/plugin-catalog-unprocessed-entities-common'; /** * {@link @backstage/core-plugin-api#ApiRef} for the {@link CatalogUnprocessedEntitiesApi} @@ -35,69 +34,24 @@ export const catalogUnprocessedEntitiesApiRef = * Response expected by the {@link CatalogUnprocessedEntitiesApi} * * @public + * @deprecated Use the type imported from `@backstage/plugin-catalog-unprocessed-entities-common` instead. */ -export type CatalogUnprocessedEntitiesApiResponse = { - entities: UnprocessedEntity[]; -}; +export type CatalogUnprocessedEntitiesApiResponse = + CommonCatalogUnprocessedEntitiesApiResponse; /** * Interface for the CatalogUnprocessedEntitiesApi. * * @public + * @deprecated Use the type imported from `@backstage/plugin-catalog-unprocessed-entities-common` instead. */ -export interface CatalogUnprocessedEntitiesApi { - /** - * Returns a list of entities with state 'pending' - */ - pending(): Promise; - /** - * Returns a list of entities with state 'failed' - */ - failed(): Promise; - /** - * Deletes an entity from the refresh_state table - */ - delete(entityId: string): Promise; -} +export interface CatalogUnprocessedEntitiesApi + extends CommonCatalogUnprocessedEntitiesApi {} /** * Default API implementation for the Catalog Unprocessed Entities plugin * * @public + * @deprecated Use the client imported from `@backstage/plugin-catalog-unprocessed-entities-common` instead. */ -export class CatalogUnprocessedEntitiesClient - implements CatalogUnprocessedEntitiesApi -{ - public discovery: DiscoveryApi; - public fetchApi: FetchApi; - - constructor(discovery: DiscoveryApi, fetchApi: FetchApi) { - this.discovery = discovery; - this.fetchApi = fetchApi; - } - - private async fetch(path: string, init?: RequestInit): Promise { - const url = await this.discovery.getBaseUrl('catalog'); - const resp = await this.fetchApi.fetch(`${url}/${path}`, init); - - if (!resp.ok) { - throw await ResponseError.fromResponse(resp); - } - - return resp.status === 204 ? (resp as T) : await resp.json(); - } - - async pending(): Promise { - return await this.fetch('entities/unprocessed/pending'); - } - - async failed(): Promise { - return await this.fetch('entities/unprocessed/failed'); - } - - async delete(entityId: string): Promise { - await this.fetch(`entities/unprocessed/delete/${entityId}`, { - method: 'DELETE', - }); - } -} +export class CatalogUnprocessedEntitiesClient extends CommonCatalogUnprocessedEntitiesClient {} diff --git a/plugins/catalog-unprocessed-entities/src/types.ts b/plugins/catalog-unprocessed-entities/src/types.ts index 65f21970f5..38b4b9dad7 100644 --- a/plugins/catalog-unprocessed-entities/src/types.ts +++ b/plugins/catalog-unprocessed-entities/src/types.ts @@ -13,45 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; +import type { + UnprocessedEntity as CommonUnprocessedEntity, + UnprocessedEntityCache as CommonUnprocessedEntityCache, + UnprocessedEntityError as CommonUnprocessedEntityError, +} from '@backstage/plugin-catalog-unprocessed-entities-common'; /** * Unprocessed entity data stored in the database. * @public + * @deprecated Use the type imported from `@backstage/plugin-catalog-unprocessed-entities-common` instead. */ -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; // remove? - errors?: UnprocessedEntityError[]; - location_key?: string; -}; +export type UnprocessedEntity = CommonUnprocessedEntity; /** * Unprocessed entity cache stored in the database. * @public + * @deprecated Use the type imported from `@backstage/plugin-catalog-unprocessed-entities-common` instead. */ -export type UnprocessedEntityCache = { - ttl: number; - cache: object; -}; +export type UnprocessedEntityCache = CommonUnprocessedEntityCache; /** * Unprocessed entity error information stored in the database. * @public + * @deprecated Use the type imported from `@backstage/plugin-catalog-unprocessed-entities-common` instead. */ -export type UnprocessedEntityError = { - name: string; - message: string; - cause: { - name: string; - message: string; - stack: string; - }; -}; +export type UnprocessedEntityError = CommonUnprocessedEntityError; diff --git a/yarn.lock b/yarn.lock index 46fb5cf160..aeaa50bee5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5289,7 +5289,9 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-unprocessed-entities-common@workspace:plugins/catalog-unprocessed-entities-common" dependencies: + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/errors": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" languageName: unknown linkType: soft @@ -5298,13 +5300,13 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-unprocessed-entities@workspace:plugins/catalog-unprocessed-entities" dependencies: - "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:^4.0.0-alpha.60"