From 29525665875e89efc6bef671e8b2f40c3b44c731 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 8 Mar 2022 17:28:28 +0100 Subject: [PATCH] catalog-model: enable usage of : and / in entity names Signed-off-by: Patrik Oldsberg --- .changeset/fuzzy-roses-swim.md | 7 ++++++ packages/catalog-model/src/entity/ref.test.ts | 23 +++++++++++++++---- packages/catalog-model/src/entity/ref.ts | 21 +++++++++++------ 3 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 .changeset/fuzzy-roses-swim.md diff --git a/.changeset/fuzzy-roses-swim.md b/.changeset/fuzzy-roses-swim.md new file mode 100644 index 0000000000..234d79c96a --- /dev/null +++ b/.changeset/fuzzy-roses-swim.md @@ -0,0 +1,7 @@ +--- +'@backstage/catalog-model': patch +--- + +Updated `parseEntityRef` to allow `:` and `/` in the entity name. For example, parsing `'component:default/foo:bar'` will result in the name `'foo:bar'`. + +Note that only parsing `'foo:bar'` itself will result in the name `'bar'` and the entity kind `'foo'`, meaning this is a particularly nasty trap for user defined entity references. For this reason it is strongly discouraged to use names that contain these characters, and the catalog model does not allow it by default. However, this change now makes is possible to use these names if the default catalog validation is replaced, and in particular a high level of automation of the catalog population can limit issues that it might otherwise cause. diff --git a/packages/catalog-model/src/entity/ref.test.ts b/packages/catalog-model/src/entity/ref.test.ts index 6cb7612439..0945eddf6e 100644 --- a/packages/catalog-model/src/entity/ref.test.ts +++ b/packages/catalog-model/src/entity/ref.test.ts @@ -34,11 +34,24 @@ describe('ref', () => { it('rejects bad inputs', () => { expect(() => parseEntityRef(null as any)).toThrow(); expect(() => parseEntityRef(7 as any)).toThrow(); - expect(() => parseEntityRef('a:b:c')).toThrow(); - expect(() => parseEntityRef('a/b/c')).toThrow(); - expect(() => parseEntityRef('a/b:c')).toThrow(); - expect(() => parseEntityRef('a:b/c/d')).toThrow(); - expect(() => parseEntityRef('a:b/c:d')).toThrow(); + }); + + it('allows names with : and /', () => { + expect( + parseEntityRef('a:b:c', { defaultKind: 'k', defaultNamespace: 'ns' }), + ).toEqual({ kind: 'a', namespace: 'ns', name: 'b:c' }); + expect( + parseEntityRef('a/b/c', { defaultKind: 'k', defaultNamespace: 'ns' }), + ).toEqual({ kind: 'k', namespace: 'a', name: 'b/c' }); + expect( + parseEntityRef('a/b:c', { defaultKind: 'k', defaultNamespace: 'ns' }), + ).toEqual({ kind: 'k', namespace: 'a', name: 'b:c' }); + expect( + parseEntityRef('a:b/c/d', { defaultKind: 'k', defaultNamespace: 'ns' }), + ).toEqual({ kind: 'a', namespace: 'b', name: 'c/d' }); + expect( + parseEntityRef('a:b/c:d', { defaultKind: 'k', defaultNamespace: 'ns' }), + ).toEqual({ kind: 'a', namespace: 'b', name: 'c:d' }); }); it('rejects empty parts in strings', () => { diff --git a/packages/catalog-model/src/entity/ref.ts b/packages/catalog-model/src/entity/ref.ts index acede5a56b..cfe979423f 100644 --- a/packages/catalog-model/src/entity/ref.ts +++ b/packages/catalog-model/src/entity/ref.ts @@ -23,18 +23,25 @@ function parseRefString(ref: string): { namespace?: string; name: string; } { - const match = /^([^:/]+:)?([^:/]+\/)?([^:/]+)$/.exec(ref.trim()); - if (!match) { + let colonI = ref.indexOf(':'); + const slashI = ref.indexOf('/'); + + // If the / is ahead of the :, treat the rest as the name + if (slashI !== -1 && slashI < colonI) { + colonI = -1; + } + + const kind = colonI === -1 ? undefined : ref.slice(0, colonI); + const namespace = slashI === -1 ? undefined : ref.slice(colonI + 1, slashI); + const name = ref.slice(Math.max(colonI + 1, slashI + 1)); + + if (kind === '' || namespace === '' || name === '') { throw new TypeError( `Entity reference "${ref}" was not on the form [:][/]`, ); } - return { - kind: match[1]?.slice(0, -1), - namespace: match[2]?.slice(0, -1), - name: match[3], - }; + return { kind, namespace, name }; } /**