frontend-plugin-api: add ID and description for RouteRef

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-12 11:35:10 +02:00
parent 49b0d0dfa9
commit a0c90a23ad
2 changed files with 55 additions and 7 deletions
@@ -15,13 +15,30 @@
*/
import { AnyRouteParams } from './types';
import { RouteRef, createRouteRef } from './RouteRef';
import { RouteRef, createRouteRef, toInternalRouteRef } from './RouteRef';
describe('RouteRef', () => {
it('should be created', () => {
it('should be created and have a mutable ID', () => {
const routeRef: RouteRef<undefined> = createRouteRef();
expect(() => routeRef.T).toThrow();
expect(String(routeRef)).toBe('RouteRef{}');
const internal = toInternalRouteRef(routeRef);
expect(() => internal.T).toThrow();
expect(internal.getParams()).toEqual([]);
expect(internal.getDescription()).toMatch(/RouteRef\.test\.ts/);
expect(String(internal)).toMatch(
/^RouteRef\{created at .*RouteRef\.test\.ts.*\}$/,
);
expect(() => internal.setId('')).toThrow(
'RouteRef id must be a non-empty string',
);
internal.setId('some-id');
expect(String(internal)).toBe('RouteRef{some-id}');
expect(() => internal.setId('some-other-id')).toThrow(
"RouteRef was referenced twice as both 'some-id' and 'some-other-id'",
);
});
it('should be created with params', () => {
@@ -31,7 +48,10 @@ describe('RouteRef', () => {
}> = createRouteRef({
params: ['x', 'y'],
});
expect(() => routeRef.T).toThrow();
const internal = toInternalRouteRef(routeRef);
expect(internal.getParams()).toEqual(['x', 'y']);
expect(internal.getDescription()).toMatch(/RouteRef\.test\.ts/);
expect(() => internal.T).toThrow();
});
it('should properly infer and validate parameter types and assignments', () => {
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { describeParentCallSite } from './describeParentCallSite';
import { AnyRouteParams } from './types';
/**
@@ -36,6 +37,9 @@ export interface InternalRouteRef<
> extends RouteRef<TParams> {
readonly version: 'v1';
getParams(): string[];
getDescription(): string;
setId(id: string): void;
}
/** @internal */
@@ -60,10 +64,13 @@ export class RouteRefImpl implements InternalRouteRef {
readonly $$type = '@backstage/RouteRef';
readonly version = 'v1';
#id?: string;
#params: string[];
#creationSite: string;
constructor(readonly params: string[] = []) {
constructor(readonly params: string[] = [], creationSite: string) {
this.#params = params;
this.#creationSite = creationSite;
}
get T(): never {
@@ -74,8 +81,27 @@ export class RouteRefImpl implements InternalRouteRef {
return this.#params;
}
getDescription(): string {
if (this.#id) {
return this.#id;
}
return `created at '${this.#creationSite}'`;
}
setId(id: string): void {
if (!id) {
throw new Error('RouteRef id must be a non-empty string');
}
if (this.#id) {
throw new Error(
`RouteRef was referenced twice as both '${this.#id}' and '${id}'`,
);
}
this.#id = id;
}
toString(): string {
return `RouteRef{}`;
return `RouteRef{${this.getDescription()}}`;
}
}
@@ -100,7 +126,9 @@ export function createRouteRef<
? TParams
: { [param in TParamKeys]: string }
> {
const creationSite = describeParentCallSite();
return new RouteRefImpl(
config?.params as string[] | undefined,
creationSite,
) as RouteRef<any>;
}