Merge pull request #31638 from ivangonzalezacuna/unprocessed-entities/move-types-to-common

[catalog-unprocessed-entities] Move types and clients from frontend to common plugin
This commit is contained in:
Ben Lambert
2025-12-02 11:08:22 +01:00
committed by GitHub
12 changed files with 355 additions and 128 deletions
+15
View File
@@ -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.
@@ -37,6 +37,8 @@
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/catalog-model": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/plugin-permission-common": "workspace:^"
},
"devDependencies": {
@@ -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<void>;
failed(
options?: UnprocessedEntitiesRequestOptions,
): Promise<CatalogUnprocessedEntitiesApiResponse>;
pending(
options?: UnprocessedEntitiesRequestOptions,
): Promise<CatalogUnprocessedEntitiesApiResponse>;
}
// @public
export type CatalogUnprocessedEntitiesApiResponse = {
entities: UnprocessedEntity[];
};
// @public
export class CatalogUnprocessedEntitiesClient
implements CatalogUnprocessedEntitiesApi
{
constructor(
discovery: {
getBaseUrl(pluginId: string): Promise<string>;
},
fetchApi?: {
fetch: typeof fetch;
},
);
// (undocumented)
delete(
entityId: string,
options?: UnprocessedEntitiesRequestOptions,
): Promise<void>;
// (undocumented)
failed(
options?: UnprocessedEntitiesRequestOptions,
): Promise<CatalogUnprocessedEntitiesApiResponse>;
// (undocumented)
pending(
options?: UnprocessedEntitiesRequestOptions,
): Promise<CatalogUnprocessedEntitiesApiResponse>;
}
// @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;
};
};
```
@@ -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<CatalogUnprocessedEntitiesApiResponse>;
/**
* Returns a list of entities with state 'failed'
*
* @param options - Additional options
*/
failed(
options?: UnprocessedEntitiesRequestOptions,
): Promise<CatalogUnprocessedEntitiesApiResponse>;
/**
* 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<void>;
}
/**
* Default API implementation for the Catalog Unprocessed Entities plugin
*
* @public
*/
export class CatalogUnprocessedEntitiesClient
implements CatalogUnprocessedEntitiesApi
{
private readonly discovery: { getBaseUrl(pluginId: string): Promise<string> };
private readonly fetchApi: { fetch: typeof fetch };
constructor(
discovery: { getBaseUrl(pluginId: string): Promise<string> },
fetchApi?: { fetch: typeof fetch },
) {
this.discovery = discovery;
this.fetchApi = fetchApi ?? { fetch };
}
private async fetch<T>(
method: string,
path: string,
options?: UnprocessedEntitiesRequestOptions,
): Promise<T> {
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<CatalogUnprocessedEntitiesApiResponse> {
return await this.fetch('GET', 'entities/unprocessed/pending', options);
}
async failed(
options?: UnprocessedEntitiesRequestOptions,
): Promise<CatalogUnprocessedEntitiesApiResponse> {
return await this.fetch('GET', 'entities/unprocessed/failed', options);
}
async delete(
entityId: string,
options?: UnprocessedEntitiesRequestOptions,
): Promise<void> {
await this.fetch(
'DELETE',
`entities/unprocessed/delete/${entityId}`,
options,
);
}
}
@@ -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';
@@ -20,4 +20,6 @@
* @packageDocumentation
*/
export * from './api';
export * from './permissions';
export * from './types';
@@ -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[];
};
@@ -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",
@@ -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<void>;
failed(): Promise<CatalogUnprocessedEntitiesApiResponse>;
pending(): Promise<CatalogUnprocessedEntitiesApiResponse>;
}
// @public @deprecated
export interface CatalogUnprocessedEntitiesApi
extends CatalogUnprocessedEntitiesApi_2 {}
// @public
export const catalogUnprocessedEntitiesApiRef: ApiRef<CatalogUnprocessedEntitiesApi>;
// @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)
```
@@ -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<CatalogUnprocessedEntitiesApiResponse>;
/**
* Returns a list of entities with state 'failed'
*/
failed(): Promise<CatalogUnprocessedEntitiesApiResponse>;
/**
* Deletes an entity from the refresh_state table
*/
delete(entityId: string): Promise<void>;
}
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<T>(path: string, init?: RequestInit): Promise<T> {
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<CatalogUnprocessedEntitiesApiResponse> {
return await this.fetch('entities/unprocessed/pending');
}
async failed(): Promise<CatalogUnprocessedEntitiesApiResponse> {
return await this.fetch('entities/unprocessed/failed');
}
async delete(entityId: string): Promise<void> {
await this.fetch(`entities/unprocessed/delete/${entityId}`, {
method: 'DELETE',
});
}
}
export class CatalogUnprocessedEntitiesClient extends CommonCatalogUnprocessedEntitiesClient {}
@@ -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;
+3 -1
View File
@@ -5291,7 +5291,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
@@ -5300,13 +5302,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"