diff --git a/.changeset/curvy-worms-exercise.md b/.changeset/curvy-worms-exercise.md new file mode 100644 index 0000000000..b88e9b5891 --- /dev/null +++ b/.changeset/curvy-worms-exercise.md @@ -0,0 +1,6 @@ +--- +'@backstage/catalog-model': patch +--- + +Deprecated `parseLocationReference` and `stringifyLocationReference`, +introducing `parseLocationRef` and `stringifyLocationRef` in their place. diff --git a/.changeset/mighty-beds-fix.md b/.changeset/mighty-beds-fix.md new file mode 100644 index 0000000000..3e5ab735dd --- /dev/null +++ b/.changeset/mighty-beds-fix.md @@ -0,0 +1,11 @@ +--- +'@backstage/catalog-client': patch +'@backstage/techdocs-common': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-github-deployments': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-todo-backend': patch +--- + +Updated to use new names for `parseLocationRef` and `stringifyLocationRef` diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index 009bc9a6cb..885797e4a8 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -22,7 +22,7 @@ import { ORIGIN_LOCATION_ANNOTATION, parseEntityRef, stringifyEntityRef, - stringifyLocationReference, + stringifyLocationRef, } from '@backstage/catalog-model'; import { ResponseError } from '@backstage/errors'; import crossFetch from 'cross-fetch'; @@ -310,7 +310,7 @@ export class CatalogClient implements CatalogApi { ); return all .map(r => r.data) - .find(l => locationCompound === stringifyLocationReference(l)); + .find(l => locationCompound === stringifyLocationRef(l)); } /** @@ -338,7 +338,7 @@ export class CatalogClient implements CatalogApi { ); return all .map(r => r.data) - .find(l => locationCompound === stringifyLocationReference(l)); + .find(l => locationCompound === stringifyLocationRef(l)); } /** diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 9cdb29a15c..9a902b0a04 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -409,6 +409,12 @@ export function parseEntityRef( }; // @public +export function parseLocationRef(ref: string): { + type: string; + target: string; +}; + +// @public @deprecated export function parseLocationReference(ref: string): { type: string; target: string; @@ -509,6 +515,12 @@ export function stringifyEntityRef( ): string; // @public +export function stringifyLocationRef(ref: { + type: string; + target: string; +}): string; + +// @public @deprecated export function stringifyLocationReference(ref: { type: string; target: string; diff --git a/packages/catalog-model/src/location/helpers.test.ts b/packages/catalog-model/src/location/helpers.test.ts index 020f7be1a1..91a2137611 100644 --- a/packages/catalog-model/src/location/helpers.test.ts +++ b/packages/catalog-model/src/location/helpers.test.ts @@ -16,47 +16,47 @@ import { getEntitySourceLocation, - parseLocationReference, - stringifyLocationReference, + parseLocationRef, + stringifyLocationRef, } from './helpers'; -describe('parseLocationReference', () => { +describe('parseLocationRef', () => { it('works for the simple case', () => { - expect(parseLocationReference('url:https://www.google.com')).toEqual({ + expect(parseLocationRef('url:https://www.google.com')).toEqual({ type: 'url', target: 'https://www.google.com', }); }); it('rejects faulty inputs', () => { - expect(() => parseLocationReference(7 as any)).toThrow( - "Unable to parse location reference '7', unexpected argument number", + expect(() => parseLocationRef(7 as any)).toThrow( + "Unable to parse location ref '7', unexpected argument number", ); - expect(() => parseLocationReference('')).toThrow( - "Unable to parse location reference '', expected ':', e.g. 'url:https://host/path'", + expect(() => parseLocationRef('')).toThrow( + "Unable to parse location ref '', expected ':', e.g. 'url:https://host/path'", ); - expect(() => parseLocationReference('hello')).toThrow( - "Unable to parse location reference 'hello', expected ':', e.g. 'url:https://host/path'", + expect(() => parseLocationRef('hello')).toThrow( + "Unable to parse location ref 'hello', expected ':', e.g. 'url:https://host/path'", ); - expect(() => parseLocationReference(':hello')).toThrow( - "Unable to parse location reference ':hello', expected ':', e.g. 'url:https://host/path'", + expect(() => parseLocationRef(':hello')).toThrow( + "Unable to parse location ref ':hello', expected ':', e.g. 'url:https://host/path'", ); - expect(() => parseLocationReference('hello:')).toThrow( - "Unable to parse location reference 'hello:', expected ':', e.g. 'url:https://host/path'", + expect(() => parseLocationRef('hello:')).toThrow( + "Unable to parse location ref 'hello:', expected ':', e.g. 'url:https://host/path'", ); - expect(() => parseLocationReference('http://blah')).toThrow( - "Invalid location reference 'http://blah', please prefix it with 'url:', e.g. 'url:http://blah'", + expect(() => parseLocationRef('http://blah')).toThrow( + "Invalid location ref 'http://blah', please prefix it with 'url:', e.g. 'url:http://blah'", ); - expect(() => parseLocationReference('https://bleh')).toThrow( - "Invalid location reference 'https://bleh', please prefix it with 'url:', e.g. 'url:https://bleh'", + expect(() => parseLocationRef('https://bleh')).toThrow( + "Invalid location ref 'https://bleh', please prefix it with 'url:', e.g. 'url:https://bleh'", ); }); }); -describe('stringifyLocationReference', () => { +describe('stringifyLocationRef', () => { it('works for the simple case', () => { expect( - stringifyLocationReference({ + stringifyLocationRef({ type: 'url', target: 'https://www.google.com', }), @@ -64,12 +64,12 @@ describe('stringifyLocationReference', () => { }); it('rejects faulty inputs', () => { - expect(() => - stringifyLocationReference({ type: '', target: 'hello' }), - ).toThrow('Unable to stringify location reference, empty type'); - expect(() => - stringifyLocationReference({ type: 'hello', target: '' }), - ).toThrow('Unable to stringify location reference, empty target'); + expect(() => stringifyLocationRef({ type: '', target: 'hello' })).toThrow( + 'Unable to stringify location ref, empty type', + ); + expect(() => stringifyLocationRef({ type: 'hello', target: '' })).toThrow( + 'Unable to stringify location ref, empty target', + ); }); }); diff --git a/packages/catalog-model/src/location/helpers.ts b/packages/catalog-model/src/location/helpers.ts index 1edd4e5ec5..76241ef208 100644 --- a/packages/catalog-model/src/location/helpers.ts +++ b/packages/catalog-model/src/location/helpers.ts @@ -26,36 +26,53 @@ import { LOCATION_ANNOTATION, SOURCE_LOCATION_ANNOTATION } from './annotation'; * @public * @param ref - A string-form location reference, e.g. `'url:https://host'` * @returns A location reference, e.g. `{ type: 'url', target: 'https://host' }` + * @deprecated use {@link parseLocationRef} instead */ -export function parseLocationReference(ref: string): { +export function parseLocationReference(ref: string) { + return parseLocationRef(ref); +} + +/** + * Parses a string form location reference. + * + * @remarks + * + * Note that the return type is not `LocationSpec`, because we do not want to + * conflate the string form with the additional properties of that type. + * + * @public + * @param ref - A string-form location ref, e.g. `'url:https://host'` + * @returns A location ref, e.g. `{ type: 'url', target: 'https://host' }` + */ +export function parseLocationRef(ref: string): { type: string; target: string; } { if (typeof ref !== 'string') { throw new TypeError( - `Unable to parse location reference '${ref}', unexpected argument ${typeof ref}`, + `Unable to parse location ref '${ref}', unexpected argument ${typeof ref}`, ); } const splitIndex = ref.indexOf(':'); if (splitIndex < 0) { throw new TypeError( - `Unable to parse location reference '${ref}', expected ':', e.g. 'url:https://host/path'`, + `Unable to parse location ref '${ref}', expected ':', e.g. 'url:https://host/path'`, ); } - const type = ref.substr(0, splitIndex).trim(); - const target = ref.substr(splitIndex + 1).trim(); + const type = ref.substring(0, splitIndex).trim(); + const target = ref.substring(splitIndex + 1).trim(); if (!type || !target) { throw new TypeError( - `Unable to parse location reference '${ref}', expected ':', e.g. 'url:https://host/path'`, + `Unable to parse location ref '${ref}', expected ':', e.g. 'url:https://host/path'`, ); } if (type === 'http' || type === 'https') { throw new TypeError( - `Invalid location reference '${ref}', please prefix it with 'url:', e.g. 'url:${ref}'`, + `Invalid location ref '${ref}', please prefix it with 'url:', e.g. 'url:${ref}'`, ); } @@ -73,17 +90,37 @@ export function parseLocationReference(ref: string): { * @public * @param ref - A location reference, e.g. `{ type: 'url', target: 'https://host' }` * @returns A string-form location reference, e.g. `'url:https://host'` + * @deprecated use {@link stringifyLocationRef} instead */ export function stringifyLocationReference(ref: { type: string; target: string; +}): string { + return stringifyLocationRef(ref); +} + +/** + * Turns a location ref into its string form. + * + * @remarks + * + * Note that the input type is not `LocationSpec`, because we do not want to + * conflate the string form with the additional properties of that type. + * + * @public + * @param ref - A location ref, e.g. `{ type: 'url', target: 'https://host' }` + * @returns A string-form location ref, e.g. `'url:https://host'` + */ +export function stringifyLocationRef(ref: { + type: string; + target: string; }): string { const { type, target } = ref; if (!type) { - throw new TypeError(`Unable to stringify location reference, empty type`); + throw new TypeError(`Unable to stringify location ref, empty type`); } else if (!target) { - throw new TypeError(`Unable to stringify location reference, empty target`); + throw new TypeError(`Unable to stringify location ref, empty target`); } return `${type}:${target}`; @@ -114,5 +151,5 @@ export function getEntitySourceLocation(entity: Entity): { ); } - return parseLocationReference(locationRef); + return parseLocationRef(locationRef); } diff --git a/packages/catalog-model/src/location/index.ts b/packages/catalog-model/src/location/index.ts index 624c1acd1d..c0e2b40c7f 100644 --- a/packages/catalog-model/src/location/index.ts +++ b/packages/catalog-model/src/location/index.ts @@ -20,8 +20,10 @@ export { SOURCE_LOCATION_ANNOTATION, } from './annotation'; export { - parseLocationReference, - stringifyLocationReference, getEntitySourceLocation, + parseLocationRef, + parseLocationReference, + stringifyLocationRef, + stringifyLocationReference, } from './helpers'; export type { Location, LocationSpec } from './types'; diff --git a/packages/techdocs-common/src/helpers.ts b/packages/techdocs-common/src/helpers.ts index 7a9e4b18be..ccf1256381 100644 --- a/packages/techdocs-common/src/helpers.ts +++ b/packages/techdocs-common/src/helpers.ts @@ -18,7 +18,7 @@ import { resolveSafeChildPath, UrlReader } from '@backstage/backend-common'; import { Entity, getEntitySourceLocation, - parseLocationReference, + parseLocationRef, } from '@backstage/catalog-model'; import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -42,7 +42,7 @@ export const parseReferenceAnnotation = ( ); } - const { type, target } = parseLocationReference(annotation); + const { type, target } = parseLocationRef(annotation); return { type: type as RemoteProtocol, target, diff --git a/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts index 54079e13ff..e7afbf720f 100644 --- a/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/AnnotateLocationEntityProcessor.ts @@ -21,7 +21,7 @@ import { LOCATION_ANNOTATION, ORIGIN_LOCATION_ANNOTATION, SOURCE_LOCATION_ANNOTATION, - stringifyLocationReference, + stringifyLocationRef, VIEW_URL_ANNOTATION, } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -58,7 +58,7 @@ export class AnnotateLocationEntityProcessor implements CatalogProcessor { }); if (sourceUrl) { - sourceLocation = stringifyLocationReference({ + sourceLocation = stringifyLocationRef({ type: 'url', target: sourceUrl, }); @@ -70,9 +70,9 @@ export class AnnotateLocationEntityProcessor implements CatalogProcessor { metadata: { annotations: pickBy( { - [LOCATION_ANNOTATION]: stringifyLocationReference(location), + [LOCATION_ANNOTATION]: stringifyLocationRef(location), [ORIGIN_LOCATION_ANNOTATION]: - stringifyLocationReference(originLocation), + stringifyLocationRef(originLocation), [VIEW_URL_ANNOTATION]: viewUrl, [EDIT_URL_ANNOTATION]: editUrl, [SOURCE_LOCATION_ANNOTATION]: sourceLocation, diff --git a/plugins/catalog-backend/src/ingestion/processors/util/parse.ts b/plugins/catalog-backend/src/ingestion/processors/util/parse.ts index c9c6c79311..a6aba7e1f9 100644 --- a/plugins/catalog-backend/src/ingestion/processors/util/parse.ts +++ b/plugins/catalog-backend/src/ingestion/processors/util/parse.ts @@ -17,7 +17,7 @@ import { Entity, LocationSpec, - stringifyLocationReference, + stringifyLocationRef, } from '@backstage/catalog-model'; import lodash from 'lodash'; import yaml from 'yaml'; @@ -32,7 +32,7 @@ export function* parseEntityYaml( try { documents = yaml.parseAllDocuments(data.toString('utf8')).filter(d => d); } catch (e) { - const loc = stringifyLocationReference(location); + const loc = stringifyLocationRef(location); const message = `Failed to parse YAML at ${loc}, ${e}`; yield result.generalError(location, message); return; @@ -40,7 +40,7 @@ export function* parseEntityYaml( for (const document of documents) { if (document.errors?.length) { - const loc = stringifyLocationReference(location); + const loc = stringifyLocationRef(location); const message = `YAML error at ${loc}, ${document.errors[0]}`; yield result.generalError(location, message); } else { diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts index 756b6a3639..105121ae2a 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts @@ -19,9 +19,9 @@ import { EntityPolicy, LocationEntity, LocationSpec, - parseLocationReference, + parseLocationRef, stringifyEntityRef, - stringifyLocationReference, + stringifyLocationRef, } from '@backstage/catalog-model'; import { assertError, @@ -119,10 +119,8 @@ export class DefaultCatalogProcessingOrchestrator // source-location? - maybe probably doesn't exist yet? const context: Context = { entityRef: stringifyEntityRef(entity), - location: parseLocationReference(getEntityLocationRef(entity)), - originLocation: parseLocationReference( - getEntityOriginLocationRef(entity), - ), + location: parseLocationRef(getEntityLocationRef(entity)), + originLocation: parseLocationRef(getEntityOriginLocationRef(entity)), cache, collector, }; @@ -149,9 +147,9 @@ export class DefaultCatalogProcessingOrchestrator throw new NotAllowedError( `Entity ${stringifyEntityRef( deferredEntity.entity, - )} at ${stringifyLocationReference( + )} at ${stringifyLocationRef( context.location, - )}, originated at ${stringifyLocationReference( + )}, originated at ${stringifyLocationRef( context.originLocation, )}, is not of an allowed kind for that location`, ); diff --git a/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts index 4ac356f706..7fe43edba1 100644 --- a/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts +++ b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts @@ -19,7 +19,7 @@ import { EntityRelationSpec, LOCATION_ANNOTATION, ORIGIN_LOCATION_ANNOTATION, - stringifyLocationReference, + stringifyLocationRef, } from '@backstage/catalog-model'; import { assertError } from '@backstage/errors'; import { Logger } from 'winston'; @@ -82,7 +82,7 @@ export class ProcessorOutputCollector { return; } - const location = stringifyLocationReference(i.location); + const location = stringifyLocationRef(i.location); // Note that at this point, we have only validated the envelope part of // the entity data. Annotations are not part of that, so we have to be diff --git a/plugins/catalog-backend/src/util/conversion.ts b/plugins/catalog-backend/src/util/conversion.ts index 559a472c25..e75abdbab0 100644 --- a/plugins/catalog-backend/src/util/conversion.ts +++ b/plugins/catalog-backend/src/util/conversion.ts @@ -21,7 +21,7 @@ import { LOCATION_ANNOTATION, ORIGIN_LOCATION_ANNOTATION, stringifyEntityRef, - stringifyLocationReference, + stringifyLocationRef, } from '@backstage/catalog-model'; import { createHash } from 'crypto'; @@ -45,7 +45,7 @@ export function locationSpecToLocationEntity( throw new Error( `Parent entity '${stringifyEntityRef( parentEntity, - )}' of location '${stringifyLocationReference( + )}' of location '${stringifyLocationRef( location, )}' does not have a location annotation`, ); @@ -57,14 +57,14 @@ export function locationSpecToLocationEntity( throw new Error( `Parent entity '${stringifyEntityRef( parentEntity, - )}' of location '${stringifyLocationReference( + )}' of location '${stringifyLocationRef( location, )}' does not have an origin location annotation`, ); } originLocation = maybeOriginLocation; } else { - ownLocation = stringifyLocationReference(location); + ownLocation = stringifyLocationRef(location); originLocation = ownLocation; } diff --git a/plugins/catalog-react/src/utils/getEntitySourceLocation.ts b/plugins/catalog-react/src/utils/getEntitySourceLocation.ts index 59c3b5ab95..1b8df5828c 100644 --- a/plugins/catalog-react/src/utils/getEntitySourceLocation.ts +++ b/plugins/catalog-react/src/utils/getEntitySourceLocation.ts @@ -16,7 +16,7 @@ import { Entity, - parseLocationReference, + parseLocationRef, SOURCE_LOCATION_ANNOTATION, } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -38,7 +38,7 @@ export function getEntitySourceLocation( } try { - const sourceLocationRef = parseLocationReference(sourceLocation); + const sourceLocationRef = parseLocationRef(sourceLocation); const integration = scmIntegrationsApi.byUrl(sourceLocationRef.target); return { locationTargetUrl: sourceLocationRef.target, diff --git a/plugins/github-deployments/src/api/index.ts b/plugins/github-deployments/src/api/index.ts index b64d914612..e0eef7cc1d 100644 --- a/plugins/github-deployments/src/api/index.ts +++ b/plugins/github-deployments/src/api/index.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { parseLocationReference } from '@backstage/catalog-model'; +import { parseLocationRef } from '@backstage/catalog-model'; import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { graphql } from '@octokit/graphql'; @@ -27,7 +27,7 @@ const getBaseUrl = ( return 'https://api.github.com'; } - const location = parseLocationReference(host); + const location = parseLocationRef(host); if (location.type !== 'github' && location.type !== 'url') { return 'https://api.github.com'; } diff --git a/plugins/scaffolder-backend/src/service/helpers.ts b/plugins/scaffolder-backend/src/service/helpers.ts index 6741ab3379..f57e7eca19 100644 --- a/plugins/scaffolder-backend/src/service/helpers.ts +++ b/plugins/scaffolder-backend/src/service/helpers.ts @@ -17,7 +17,7 @@ import { Entity, LOCATION_ANNOTATION, - parseLocationReference, + parseLocationRef, SOURCE_LOCATION_ANNOTATION, } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; @@ -67,7 +67,7 @@ export function getEntityBaseUrl(entity: Entity): string | undefined { return undefined; } - const { type, target } = parseLocationReference(location); + const { type, target } = parseLocationRef(location); if (type === 'url') { return target; } else if (type === 'file') { diff --git a/plugins/todo-backend/src/service/TodoReaderService.ts b/plugins/todo-backend/src/service/TodoReaderService.ts index 54957d89c4..8fbca4019f 100644 --- a/plugins/todo-backend/src/service/TodoReaderService.ts +++ b/plugins/todo-backend/src/service/TodoReaderService.ts @@ -21,7 +21,7 @@ import { SOURCE_LOCATION_ANNOTATION, serializeEntityRef, Entity, - parseLocationReference, + parseLocationRef, } from '@backstage/catalog-model'; import { TodoReader } from '../lib'; import { ListTodosRequest, ListTodosResponse, TodoService } from './types'; @@ -130,7 +130,7 @@ export class TodoReaderService implements TodoService { const sourceLocation = entity.metadata.annotations?.[SOURCE_LOCATION_ANNOTATION]; if (sourceLocation) { - const parsed = parseLocationReference(sourceLocation); + const parsed = parseLocationRef(sourceLocation); if (parsed.type !== 'url') { throw new InputError( `Invalid entity source location type for ${serializeEntityRef( @@ -143,7 +143,7 @@ export class TodoReaderService implements TodoService { const location = entity.metadata.annotations?.[LOCATION_ANNOTATION]; if (location) { - const parsed = parseLocationReference(location); + const parsed = parseLocationRef(location); if (parsed.type !== 'url') { throw new InputError( `Invalid entity location type for ${serializeEntityRef(