frontend-plugin-api: refactor ApiBlueprint to use advanced param types

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-07-28 15:13:18 +02:00
parent 39e8111a30
commit fa7861ddc5
2 changed files with 110 additions and 19 deletions
@@ -16,21 +16,19 @@
import { createExtensionInput } from '../wiring';
import { ApiBlueprint } from './ApiBlueprint';
import { createApiFactory, createApiRef } from '@backstage/core-plugin-api';
import { createApiRef } from '@backstage/core-plugin-api';
describe('ApiBlueprint', () => {
it('should create an extension with sensible defaults', () => {
const api = createApiRef<{ foo: string }>({ id: 'test' });
const factory = createApiFactory({
api,
deps: {},
factory: () => ({ foo: 'bar' }),
});
const extension = ApiBlueprint.make({
params: {
factory,
},
params: define =>
define({
api,
deps: {},
factory: () => ({ foo: 'bar' }),
}),
name: 'test',
});
@@ -58,6 +56,97 @@ describe('ApiBlueprint', () => {
`);
});
it('should properly type the API factory', () => {
const fooApi = createApiRef<{ foo: string }>({ id: 'foo' });
const barApi = createApiRef<{ bar: string }>({ id: 'bar' });
expect('test').not.toBe('failing without assertions');
ApiBlueprint.make({
params: define =>
define({ api: fooApi, deps: {}, factory: () => ({ foo: 'foo' }) }),
});
ApiBlueprint.make({
params: define =>
define({
api: fooApi,
deps: {},
// @ts-expect-error missing property
factory: () => ({}),
}),
});
ApiBlueprint.make({
params: define =>
define({
api: fooApi,
deps: {},
// @ts-expect-error wrong property
factory: () => ({
bar: 'bar',
}),
}),
});
ApiBlueprint.make({
params: define =>
define({
api: fooApi,
deps: {},
factory: () => ({
// @ts-expect-error wrong type
foo: 1,
}),
}),
});
ApiBlueprint.make({
params: define =>
define({
api: fooApi,
deps: { bar: barApi },
factory: ({ bar }) => ({ foo: bar.bar }),
}),
});
ApiBlueprint.make({
params: define =>
define({
api: fooApi,
deps: { bar: barApi },
factory: ({ bar }) => ({
// @ts-expect-error not an available property
foo: bar.foo,
}),
}),
});
ApiBlueprint.make({
params: define =>
define({
api: fooApi,
deps: { bar: barApi },
factory: ({ bar }) => ({
// @ts-expect-error not an available property
foo: bar.foo,
}),
}),
});
ApiBlueprint.make({
params: define =>
define({
api: fooApi,
deps: {},
factory: ({ bar }) => ({
// @ts-expect-error unknown dep
foo: bar.bar,
}),
}),
});
});
it('should create an extension with custom factory', () => {
const api = createApiRef<{ foo: string }>({ id: 'test' });
const factory = jest.fn(() => ({ foo: 'bar' }));
@@ -73,13 +162,7 @@ describe('ApiBlueprint', () => {
},
name: api.id,
factory(originalFactory, { config: _config, inputs: _inputs }) {
return originalFactory({
factory: createApiFactory({
api,
deps: {},
factory,
}),
});
return originalFactory(define => define({ api, deps: {}, factory }));
},
});
@@ -14,8 +14,9 @@
* limitations under the License.
*/
import { AnyApiFactory, ApiFactory } from '../apis/system';
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
import { AnyApiFactory } from '@backstage/core-plugin-api';
import { createExtensionBlueprintParams } from '../wiring/createExtensionBlueprint';
const factoryDataRef = createExtensionDataRef<AnyApiFactory>().with({
id: 'core.api.factory',
@@ -33,7 +34,14 @@ export const ApiBlueprint = createExtensionBlueprint({
dataRefs: {
factory: factoryDataRef,
},
*factory(params: { factory: AnyApiFactory }) {
yield factoryDataRef(params.factory);
defineParams: <
TApi,
TImpl extends TApi,
TDeps extends { [name in string]: unknown },
>(
params: ApiFactory<TApi, TImpl, TDeps>,
) => createExtensionBlueprintParams(params as AnyApiFactory),
*factory(params) {
yield factoryDataRef(params);
},
});