frontend-plugin-api: use api ref ID as namespace + allow namespace override for plugins

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-11-24 14:26:01 +01:00
parent c1d5dfbe13
commit 1b241c3901
12 changed files with 85 additions and 106 deletions
+17 -12
View File
@@ -376,18 +376,23 @@ export type CoreProgressComponent = ComponentType<PropsWithChildren<{}>>;
export function createApiExtension<
TConfig extends {},
TInputs extends AnyExtensionInputMap,
>(options: {
factory:
| AnyApiFactory
| ((options: {
config: TConfig;
inputs: Expand<ExtensionInputValues<TInputs>>;
}) => AnyApiFactory);
namespace?: string;
name?: string;
configSchema?: PortableSchema<TConfig>;
inputs?: TInputs;
}): ExtensionDefinition<TConfig>;
>(
options: (
| {
api: AnyApiRef;
factory: (options: {
config: TConfig;
inputs: Expand<ExtensionInputValues<TInputs>>;
}) => AnyApiFactory;
}
| {
factory: AnyApiFactory;
}
) & {
configSchema?: PortableSchema<TConfig>;
inputs?: TInputs;
},
): ExtensionDefinition<TConfig>;
export { createApiFactory };
@@ -33,31 +33,7 @@ describe('createApiExtension', () => {
).toEqual({
$$type: '@backstage/ExtensionDefinition',
kind: 'api',
attachTo: { id: 'core', input: 'apis' },
disabled: false,
configSchema: undefined,
inputs: {},
output: {
api: expect.objectContaining({
$$type: '@backstage/ExtensionDataRef',
id: 'core.api.factory',
config: {},
}),
},
factory: expect.any(Function),
});
expect(
createApiExtension({
factory,
namespace: 'ns',
name: 'n',
}),
).toEqual({
$$type: '@backstage/ExtensionDefinition',
kind: 'api',
namespace: 'ns',
name: 'n',
namespace: 'test',
attachTo: { id: 'core', input: 'apis' },
disabled: false,
configSchema: undefined,
@@ -78,6 +54,7 @@ describe('createApiExtension', () => {
const factory = jest.fn(() => ({ foo: 'bar' }));
const extension = createApiExtension({
api,
inputs: {},
factory({ config: _config, inputs: _inputs }) {
return createApiFactory({
@@ -91,6 +68,7 @@ describe('createApiExtension', () => {
expect(extension).toEqual({
$$type: '@backstage/ExtensionDefinition',
kind: 'api',
namespace: 'test',
attachTo: { id: 'core', input: 'apis' },
disabled: false,
configSchema: undefined,
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { AnyApiFactory } from '@backstage/core-plugin-api';
import { AnyApiFactory, AnyApiRef } from '@backstage/core-plugin-api';
import { PortableSchema } from '../schema';
import {
ExtensionInputValues,
@@ -28,24 +28,33 @@ import { Expand } from '../types';
export function createApiExtension<
TConfig extends {},
TInputs extends AnyExtensionInputMap,
>(options: {
factory:
| AnyApiFactory
| ((options: {
config: TConfig;
inputs: Expand<ExtensionInputValues<TInputs>>;
}) => AnyApiFactory);
namespace?: string;
name?: string;
configSchema?: PortableSchema<TConfig>;
inputs?: TInputs;
}) {
>(
options: (
| {
api: AnyApiRef;
factory: (options: {
config: TConfig;
inputs: Expand<ExtensionInputValues<TInputs>>;
}) => AnyApiFactory;
}
| {
factory: AnyApiFactory;
}
) & {
configSchema?: PortableSchema<TConfig>;
inputs?: TInputs;
},
) {
const { factory, configSchema, inputs: extensionInputs } = options;
const apiRef =
'api' in options ? options.api : (factory as { api: AnyApiRef }).api;
return createExtension({
kind: 'api',
namespace: options.namespace,
name: options.name,
// Since ApiRef IDs use a global namespace we use the namespace here in order to override
// potential plugin IDs and always end up with the format `api:<api-ref-id>`
namespace: apiRef.id,
attachTo: { id: 'core', input: 'apis' },
inputs: extensionInputs,
configSchema,
@@ -22,7 +22,7 @@ export function resolveExtensionDefinition<TConfig>(
context?: { namespace?: string },
): Extension<TConfig> {
const { name, kind, namespace: _, ...rest } = definition;
const namespace = context?.namespace ?? definition.namespace;
const namespace = definition.namespace ?? context?.namespace;
const namePart =
name && namespace ? `${namespace}/${name}` : namespace || name;