diff --git a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts index 3dc08a81c3..761a0f8c0a 100644 --- a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts +++ b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts @@ -18,12 +18,11 @@ import { Logger } from 'winston'; import parseGitUrl from 'git-url-parse'; import { Entity } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; +import { LocationAnalyzer, ScmLocationAnalyzer } from './types'; import { AnalyzeLocationRequest, AnalyzeLocationResponse, - LocationAnalyzer, - ScmLocationAnalyzer, -} from './types'; +} from '@backstage/plugin-catalog-common'; export class RepoLocationAnalyzer implements LocationAnalyzer { private readonly logger: Logger; diff --git a/plugins/catalog-backend/src/ingestion/index.ts b/plugins/catalog-backend/src/ingestion/index.ts index 0b00809043..0fc71f6785 100644 --- a/plugins/catalog-backend/src/ingestion/index.ts +++ b/plugins/catalog-backend/src/ingestion/index.ts @@ -15,11 +15,6 @@ */ export type { - AnalyzeLocationEntityField, - AnalyzeLocationExistingEntity, - AnalyzeLocationGenerateEntity, - AnalyzeLocationRequest, - AnalyzeLocationResponse, LocationAnalyzer, ScmLocationAnalyzer, AnalyzeOptions, diff --git a/plugins/catalog-backend/src/ingestion/types.ts b/plugins/catalog-backend/src/ingestion/types.ts index c5237af6d3..c6a480a7f5 100644 --- a/plugins/catalog-backend/src/ingestion/types.ts +++ b/plugins/catalog-backend/src/ingestion/types.ts @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { Entity } from '@backstage/catalog-model'; -import { RecursivePartial } from '../util/RecursivePartial'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { + AnalyzeLocationResponse, + AnalyzeLocationRequest, + AnalyzeLocationExistingEntity, +} from '@backstage/plugin-catalog-common'; /** @public */ export type LocationAnalyzer = { @@ -31,75 +32,6 @@ export type LocationAnalyzer = { ): Promise; }; -/** @public */ -export type AnalyzeLocationRequest = { - location: LocationSpec; - catalogFilename?: string; -}; - -/** @public */ -export type AnalyzeLocationResponse = { - existingEntityFiles: AnalyzeLocationExistingEntity[]; - generateEntities: AnalyzeLocationGenerateEntity[]; -}; - -/** - * If the folder pointed to already contained catalog info yaml files, they are - * read and emitted like this so that the frontend can inform the user that it - * located them and can make sure to register them as well if they weren't - * already - * @public - */ -export type AnalyzeLocationExistingEntity = { - location: LocationSpec; - isRegistered: boolean; - entity: Entity; -}; - -/** - * This is some form of representation of what the analyzer could deduce. - * We should probably have a chat about how this can best be conveyed to - * the frontend. It'll probably contain a (possibly incomplete) entity, plus - * enough info for the frontend to know what form data to show to the user - * for overriding/completing the info. - * @public - */ -export type AnalyzeLocationGenerateEntity = { - // Some form of partial representation of the entity - entity: RecursivePartial; - // Lists the suggestions that the user may want to override - fields: AnalyzeLocationEntityField[]; -}; - -// This is where I get really vague. Something like this perhaps? Or it could be -// something like a json-schema that contains enough info for the frontend to -// be able to present a form and explanations -/** @public */ -export type AnalyzeLocationEntityField = { - /** - * e.g. "spec.owner"? The frontend needs to know how to "inject" the field into the - * entity again if the user wants to change it - */ - field: string; - - /** The outcome of the analysis for this particular field */ - state: - | 'analysisSuggestedValue' - | 'analysisSuggestedNoValue' - | 'needsUserInput'; - - // If the analysis did suggest a value, this is where it would be. Not sure if we want - // to limit this to strings or if we want it to be any JsonValue - value: string | null; - /** - * A text to show to the user to inform about the choices made. Like, it could say - * "Found a CODEOWNERS file that covers this target, so we suggest leaving this - * field empty; which would currently make it owned by X" where X is taken from the - * codeowners file. - */ - description: string; -}; - /** @public */ export type AnalyzeOptions = { url: string; diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index ecb53d35de..96e9f31b43 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -33,6 +33,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { + "@backstage/catalog-model": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-search-common": "workspace:^" }, diff --git a/plugins/catalog-common/src/index.ts b/plugins/catalog-common/src/index.ts index 614948e56c..a37fd122f9 100644 --- a/plugins/catalog-common/src/index.ts +++ b/plugins/catalog-common/src/index.ts @@ -35,3 +35,4 @@ export { export type { CatalogEntityPermission } from './permissions'; export * from './search'; +export * from './ingestion'; diff --git a/plugins/catalog-common/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-common/src/ingestion/LocationAnalyzer.ts new file mode 100644 index 0000000000..8d570894cf --- /dev/null +++ b/plugins/catalog-common/src/ingestion/LocationAnalyzer.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2022 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 { LocationSpec } from '@backstage/plugin-catalog-node'; +import { Entity } from '@backstage/catalog-model'; +import { RecursivePartial } from './RecursivePartial'; + +/** @public */ +export type AnalyzeLocationRequest = { + location: LocationSpec; + catalogFilename?: string; +}; + +/** @public */ +export type AnalyzeLocationResponse = { + existingEntityFiles: AnalyzeLocationExistingEntity[]; + generateEntities: AnalyzeLocationGenerateEntity[]; +}; + +/** + * If the folder pointed to already contained catalog info yaml files, they are + * read and emitted like this so that the frontend can inform the user that it + * located them and can make sure to register them as well if they weren't + * already + * @public + */ +export type AnalyzeLocationExistingEntity = { + location: LocationSpec; + isRegistered: boolean; + entity: Entity; +}; + +/** + * This is some form of representation of what the analyzer could deduce. + * We should probably have a chat about how this can best be conveyed to + * the frontend. It'll probably contain a (possibly incomplete) entity, plus + * enough info for the frontend to know what form data to show to the user + * for overriding/completing the info. + * @public + */ +export type AnalyzeLocationGenerateEntity = { + // Some form of partial representation of the entity + entity: RecursivePartial; + // Lists the suggestions that the user may want to override + fields: AnalyzeLocationEntityField[]; +}; + +// This is where I get really vague. Something like this perhaps? Or it could be +// something like a json-schema that contains enough info for the frontend to +// be able to present a form and explanations +/** @public */ +export type AnalyzeLocationEntityField = { + /** + * e.g. "spec.owner"? The frontend needs to know how to "inject" the field into the + * entity again if the user wants to change it + */ + field: string; + + /** The outcome of the analysis for this particular field */ + state: + | 'analysisSuggestedValue' + | 'analysisSuggestedNoValue' + | 'needsUserInput'; + + // If the analysis did suggest a value, this is where it would be. Not sure if we want + // to limit this to strings or if we want it to be any JsonValue + value: string | null; + /** + * A text to show to the user to inform about the choices made. Like, it could say + * "Found a CODEOWNERS file that covers this target, so we suggest leaving this + * field empty; which would currently make it owned by X" where X is taken from the + * codeowners file. + */ + description: string; +}; diff --git a/plugins/catalog-common/src/ingestion/RecursivePartial.test.ts b/plugins/catalog-common/src/ingestion/RecursivePartial.test.ts new file mode 100644 index 0000000000..ab8d50534e --- /dev/null +++ b/plugins/catalog-common/src/ingestion/RecursivePartial.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2020 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 { RecursivePartial } from './RecursivePartial'; + +describe('RecursivePartial', () => { + it('is recursive', () => { + type X = { + required: { + required: string; + }; + }; + const x: RecursivePartial = { + required: {}, + }; + expect(x).toEqual({ required: {} }); + }); +}); diff --git a/plugins/catalog-common/src/ingestion/RecursivePartial.ts b/plugins/catalog-common/src/ingestion/RecursivePartial.ts new file mode 100644 index 0000000000..c452836f34 --- /dev/null +++ b/plugins/catalog-common/src/ingestion/RecursivePartial.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2020 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. + */ + +/** + * Makes all keys of an entire hierarchy optional. + * @ignore + */ +export type RecursivePartial = { + [P in keyof T]?: T[P] extends (infer U)[] + ? RecursivePartial[] + : T[P] extends object + ? RecursivePartial + : T[P]; +}; diff --git a/plugins/catalog-common/src/ingestion/index.ts b/plugins/catalog-common/src/ingestion/index.ts new file mode 100644 index 0000000000..ceb2ff1dfc --- /dev/null +++ b/plugins/catalog-common/src/ingestion/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2022 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 type { + AnalyzeLocationResponse, + AnalyzeLocationRequest, + AnalyzeLocationExistingEntity, +} from './LocationAnalyzer'; diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 055955c9c7..224a30bdb2 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -40,7 +40,7 @@ "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/integration-react": "workspace:^", - "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/plugins/catalog-import/src/api/CatalogImportClient.ts b/plugins/catalog-import/src/api/CatalogImportClient.ts index d019b40045..0663122253 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.ts @@ -30,7 +30,7 @@ import { Base64 } from 'js-base64'; import { AnalyzeResult, CatalogImportApi } from './CatalogImportApi'; import { getGithubIntegrationConfig } from './GitHub'; import { getBranchName, getCatalogFilename } from '../components/helpers'; -import { AnalyzeLocationResponse } from '@backstage/plugin-catalog-backend'; +import { AnalyzeLocationResponse } from '@backstage/plugin-catalog-common'; import { CompoundEntityRef } from '@backstage/catalog-model'; /** diff --git a/yarn.lock b/yarn.lock index 5563c9ffbe..71d139f97d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4718,7 +4718,9 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-common@workspace:plugins/catalog-common" dependencies: + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-search-common": "workspace:^" languageName: unknown @@ -4799,7 +4801,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/integration-react": "workspace:^" - "@backstage/plugin-catalog-backend": "workspace:^" + "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@material-ui/core": ^4.12.2