Merge pull request #29445 from tklever/entity-route-params-refactor
refactor: consolidate entity route param extraction in catalog-react
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': minor
|
||||
---
|
||||
|
||||
`entityRouteParams` now also accepts entity refs, and can help with encoding the resulting parameters.
|
||||
@@ -524,12 +524,20 @@ export interface EntityRefPresentationSnapshot {
|
||||
}
|
||||
|
||||
// @public
|
||||
export function entityRouteParams(entity: Entity): {
|
||||
export function entityRouteParams(
|
||||
entityOrRef: Entity | CompoundEntityRef | string,
|
||||
options?: EntityRouteParamsOptions,
|
||||
): {
|
||||
readonly kind: string;
|
||||
readonly namespace: string;
|
||||
readonly name: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type EntityRouteParamsOptions = {
|
||||
encodeParams?: boolean;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const entityRouteRef: RouteRef<{
|
||||
name: string;
|
||||
|
||||
@@ -14,16 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompoundEntityRef,
|
||||
DEFAULT_NAMESPACE,
|
||||
Entity,
|
||||
parseEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
|
||||
import { Link, LinkProps } from '@backstage/core-components';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { ReactNode, forwardRef } from 'react';
|
||||
import { entityRouteRef } from '../../routes';
|
||||
import { entityRouteParams, entityRouteRef } from '../../routes';
|
||||
import { EntityDisplayName } from '../EntityDisplayName';
|
||||
|
||||
/**
|
||||
@@ -88,33 +83,7 @@ function useEntityRoute(
|
||||
): string {
|
||||
const entityRoute = useRouteRef(entityRouteRef);
|
||||
|
||||
let kind;
|
||||
let namespace;
|
||||
let name;
|
||||
|
||||
if (typeof entityRef === 'string') {
|
||||
const parsed = parseEntityRef(entityRef);
|
||||
kind = parsed.kind;
|
||||
namespace = parsed.namespace;
|
||||
name = parsed.name;
|
||||
} else if ('metadata' in entityRef) {
|
||||
kind = entityRef.kind;
|
||||
namespace = entityRef.metadata.namespace;
|
||||
name = entityRef.metadata.name;
|
||||
} else {
|
||||
kind = entityRef.kind;
|
||||
namespace = entityRef.namespace;
|
||||
name = entityRef.name;
|
||||
}
|
||||
|
||||
kind = kind.toLocaleLowerCase('en-US');
|
||||
namespace = namespace?.toLocaleLowerCase('en-US') ?? DEFAULT_NAMESPACE;
|
||||
|
||||
const routeParams = {
|
||||
kind: encodeURIComponent(kind),
|
||||
namespace: encodeURIComponent(namespace),
|
||||
name: encodeURIComponent(name),
|
||||
};
|
||||
const routeParams = entityRouteParams(entityRef, { encodeParams: true });
|
||||
|
||||
return entityRoute(routeParams);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ export * from './apis';
|
||||
export * from './components';
|
||||
export * from './hooks';
|
||||
export * from './filters';
|
||||
export type { EntityRouteParamsOptions } from './routes';
|
||||
export { entityRouteParams, entityRouteRef } from './routes';
|
||||
export * from './types';
|
||||
export * from './overridableComponents';
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
Entity,
|
||||
getCompoundEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { entityRouteParams } from './routes';
|
||||
|
||||
const entity: Entity = {
|
||||
apiVersion: '1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'Test-Component',
|
||||
},
|
||||
};
|
||||
|
||||
const expectedEntityRouteParams = {
|
||||
kind: 'component',
|
||||
name: 'Test-Component',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
};
|
||||
|
||||
const namespacedEntity = {
|
||||
apiVersion: '1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'Test-Namespaced-Component',
|
||||
namespace: 'Test-Namespace',
|
||||
},
|
||||
};
|
||||
|
||||
const expectedNamespacedEntityRouteParams = {
|
||||
kind: 'component',
|
||||
name: 'Test-Namespaced-Component',
|
||||
namespace: 'test-namespace',
|
||||
};
|
||||
|
||||
describe('entityRouteParams', () => {
|
||||
it.each([
|
||||
['Entity', entity, expectedEntityRouteParams],
|
||||
['ComponentRef', getCompoundEntityRef(entity), expectedEntityRouteParams],
|
||||
['string', 'component:Test-Component', expectedEntityRouteParams],
|
||||
[
|
||||
'namespaced Entity',
|
||||
namespacedEntity,
|
||||
expectedNamespacedEntityRouteParams,
|
||||
],
|
||||
[
|
||||
'namespaced ComponentRef',
|
||||
getCompoundEntityRef(namespacedEntity),
|
||||
expectedNamespacedEntityRouteParams,
|
||||
],
|
||||
[
|
||||
'namespaced string',
|
||||
'component:Test-Namespace/Test-Namespaced-Component',
|
||||
expectedNamespacedEntityRouteParams,
|
||||
],
|
||||
])(
|
||||
'should return correct route params for %s',
|
||||
(_type, entityOrRef, expectedRouteParams) => {
|
||||
const actualRouteParams = entityRouteParams(entityOrRef);
|
||||
expect(actualRouteParams).toEqual(expectedRouteParams);
|
||||
},
|
||||
);
|
||||
|
||||
it('should not encode route params by default', () => {
|
||||
const actualRouteParams = entityRouteParams(
|
||||
'Custom Entity:Test Namespace/Test Component',
|
||||
);
|
||||
expect(actualRouteParams).toEqual({
|
||||
kind: 'custom entity',
|
||||
name: 'Test Component',
|
||||
namespace: 'test namespace',
|
||||
});
|
||||
});
|
||||
|
||||
it('should encode route params', () => {
|
||||
const actualRouteParams = entityRouteParams(
|
||||
'Custom Entity:Test Namespace/Test Component',
|
||||
{ encodeParams: true },
|
||||
);
|
||||
expect(actualRouteParams).toEqual({
|
||||
kind: 'custom%20entity',
|
||||
name: 'Test%20Component',
|
||||
namespace: 'test%20namespace',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -14,7 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
|
||||
import {
|
||||
Entity,
|
||||
DEFAULT_NAMESPACE,
|
||||
CompoundEntityRef,
|
||||
parseEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { createRouteRef } from '@backstage/core-plugin-api';
|
||||
import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
|
||||
|
||||
@@ -37,16 +42,54 @@ export const entityRouteRef = getOrCreateGlobalSingleton(
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* Configurable options for `entityRouteParams`
|
||||
* @public
|
||||
*/
|
||||
export type EntityRouteParamsOptions = {
|
||||
encodeParams?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function to get suitable route params for entityRoute, given an
|
||||
* @public
|
||||
*/
|
||||
export function entityRouteParams(entity: Entity) {
|
||||
export function entityRouteParams(
|
||||
entityOrRef: Entity | CompoundEntityRef | string,
|
||||
options?: EntityRouteParamsOptions,
|
||||
) {
|
||||
let kind;
|
||||
let namespace;
|
||||
let name;
|
||||
|
||||
if (typeof entityOrRef === 'string') {
|
||||
const parsed = parseEntityRef(entityOrRef);
|
||||
kind = parsed.kind;
|
||||
namespace = parsed.namespace;
|
||||
name = parsed.name;
|
||||
} else if ('metadata' in entityOrRef) {
|
||||
kind = entityOrRef.kind;
|
||||
namespace = entityOrRef.metadata.namespace;
|
||||
name = entityOrRef.metadata.name;
|
||||
} else {
|
||||
kind = entityOrRef.kind;
|
||||
namespace = entityOrRef.namespace;
|
||||
name = entityOrRef.name;
|
||||
}
|
||||
|
||||
kind = kind.toLocaleLowerCase('en-US');
|
||||
namespace = namespace?.toLocaleLowerCase('en-US') ?? DEFAULT_NAMESPACE;
|
||||
|
||||
const { encodeParams = false } = options || {};
|
||||
if (encodeParams) {
|
||||
kind = encodeURIComponent(kind);
|
||||
namespace = encodeURIComponent(namespace);
|
||||
name = encodeURIComponent(name);
|
||||
}
|
||||
|
||||
return {
|
||||
kind: entity.kind.toLocaleLowerCase('en-US'),
|
||||
namespace:
|
||||
entity.metadata.namespace?.toLocaleLowerCase('en-US') ??
|
||||
DEFAULT_NAMESPACE,
|
||||
name: entity.metadata.name,
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
} as const;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user