chore: updating API blueprint

Signed-off-by: blam <ben@blam.sh>

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2024-08-08 13:15:56 +02:00
parent 7a4eb9bcb5
commit d3cfdc6e4d
9 changed files with 176 additions and 145 deletions
@@ -32,7 +32,27 @@ describe('ApiBlueprint', () => {
},
});
expect(extension).toMatchInlineSnapshot();
expect(extension).toMatchInlineSnapshot(`
{
"$$type": "@backstage/ExtensionDefinition",
"attachTo": {
"id": "app",
"input": "apis",
},
"configSchema": undefined,
"disabled": false,
"factory": [Function],
"inputs": {},
"kind": "api",
"name": undefined,
"namespace": "test",
"output": [
[Function],
],
"toString": [Function],
"version": "v2",
}
`);
});
it('should create an extension with custom factory', () => {
@@ -48,19 +68,62 @@ describe('ApiBlueprint', () => {
inputs: {
test: createExtensionInput([ApiBlueprint.dataRefs.factory]),
},
namespace: api.id,
factory(originalFactory, { config: _config, inputs: _inputs }) {
return originalFactory({
api,
factory: () =>
createApiFactory({
api,
deps: {},
factory,
}),
factory: createApiFactory({
api,
deps: {},
factory,
}),
});
},
});
expect(extension).toMatchInlineSnapshot();
expect(extension).toMatchInlineSnapshot(`
{
"$$type": "@backstage/ExtensionDefinition",
"attachTo": {
"id": "app",
"input": "apis",
},
"configSchema": {
"parse": [Function],
"schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"test": {
"default": "test",
"type": "string",
},
},
"type": "object",
},
},
"disabled": false,
"factory": [Function],
"inputs": {
"test": {
"$$type": "@backstage/ExtensionInput",
"config": {
"optional": false,
"singleton": false,
},
"extensionData": [
[Function],
],
},
},
"kind": "api",
"name": undefined,
"namespace": "test",
"output": [
[Function],
],
"toString": [Function],
"version": "v2",
}
`);
});
});
@@ -15,7 +15,7 @@
*/
import { createExtensionBlueprint } from '../wiring';
import { createApiExtension } from './createApiExtension';
import { AnyApiFactory, AnyApiRef } from '@backstage/core-plugin-api';
import { AnyApiFactory } from '@backstage/core-plugin-api';
export const ApiBlueprint = createExtensionBlueprint({
kind: 'api',
@@ -24,27 +24,8 @@ export const ApiBlueprint = createExtensionBlueprint({
dataRefs: {
factory: createApiExtension.factoryDataRef,
},
*factory(
params: // remove this form.
| {
api: AnyApiRef;
factory: (params: unknown) => AnyApiFactory;
}
| {
factory: AnyApiFactory;
},
{ config, inputs },
) {
yield createApiExtension.factoryDataRef(
typeof params.factory === 'function'
? params.factory({ config, inputs })
: params.factory,
);
},
namespace: params => {
const apiRef =
'api' in params ? params.api : (params.factory as { api: AnyApiRef }).api;
return apiRef.id;
*factory(params: { factory: AnyApiFactory }) {
yield createApiExtension.factoryDataRef(params.factory);
},
namespace: ({ factory }) => factory.api.id,
});
@@ -0,0 +1,48 @@
/*
* Copyright 2024 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 React from 'react';
import { AppRootElementBlueprint } from './AppRootElementBlueprint';
describe('AppRootElementBlueprint', () => {
it('should create an extension with sensible defaults', () => {
const extension = AppRootElementBlueprint.make({
params: {
element: <div />,
},
});
expect(extension).toMatchInlineSnapshot(`
{
"$$type": "@backstage/ExtensionDefinition",
"attachTo": {
"id": "app/root",
"input": "elements",
},
"configSchema": undefined,
"disabled": false,
"factory": [Function],
"inputs": {},
"kind": "app-root-element",
"name": undefined,
"namespace": undefined,
"output": [
[Function],
],
"toString": [Function],
"version": "v2",
}
`);
});
});
@@ -0,0 +1,27 @@
/*
* Copyright 2024 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 { coreExtensionData, createExtensionBlueprint } from '../wiring';
export const AppRootElementBlueprint = createExtensionBlueprint({
kind: 'app-root-element',
attachTo: { id: 'app/root', input: 'elements' },
output: [coreExtensionData.reactElement],
*factory(params: { element: JSX.Element | (() => JSX.Element) }) {
yield coreExtensionData.reactElement(
typeof params.element === 'function' ? params.element() : params.element,
);
},
});
@@ -1,69 +0,0 @@
/*
* Copyright 2024 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 { ComponentRef } from '../components';
import { createExtensionBlueprint } from '../wiring';
import { createComponentExtension } from './createComponentExtension';
import { lazy, ComponentType } from 'react';
// this is hard to do with blueprints... no TProps for the elements
export const ComponentBlueprint = createExtensionBlueprint({
kind: 'component',
attachTo: { id: 'app', input: 'components' },
output: [createComponentExtension.componentDataRef],
dataRefs: {
component: createComponentExtension.componentDataRef,
},
factory(
{
ref,
loader,
}: {
ref: ComponentRef<any>;
loader:
| {
lazy: (values: any) => Promise<ComponentType<any>>;
}
| {
sync: (values: any) => ComponentType<any>;
};
},
{ config, inputs },
) {
if ('sync' in loader) {
return [
createComponentExtension.componentDataRef({
ref,
impl: loader.sync({ config, inputs }),
}),
];
}
const lazyLoader = loader.lazy;
const ExtensionComponent = lazy(() =>
lazyLoader({ config, inputs }).then(Component => ({
default: Component,
})),
);
return [
createComponentExtension.componentDataRef({
ref,
impl: ExtensionComponent,
}),
];
},
});
@@ -107,16 +107,18 @@ describe('RouterBlueprint', () => {
inputs: {
children: createExtensionInput([coreExtensionData.reactElement]),
},
params: {
Component: ({ inputs, children, config }) => (
<MemoryRouter>
<div
data-testid={`test-router-${config.name}-${inputs.children.length}`}
>
{children}
</div>
</MemoryRouter>
),
*factory(originalFactory, { inputs, config }) {
yield* originalFactory({
Component: ({ children }) => (
<MemoryRouter>
<div
data-testid={`test-router-${config.name}-${inputs.children.length}`}
>
{children}
</div>
</MemoryRouter>
),
});
},
});
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { ComponentType, PropsWithChildren } from 'react';
import { ComponentType, PropsWithChildren } from 'react';
import { createExtensionBlueprint } from '../wiring';
import { createRouterExtension } from './createRouterExtension';
@@ -24,25 +24,7 @@ export const RouterBlueprint = createExtensionBlueprint({
dataRefs: {
component: createRouterExtension.componentDataRef,
},
*factory(
{
Component,
}: {
Component: ComponentType<
PropsWithChildren<{
inputs: typeof inputs;
config: typeof config;
}>
>;
},
{ config, inputs },
) {
const Wrapper = (props: PropsWithChildren<{}>) => (
<Component inputs={inputs} config={config}>
{props.children}
</Component>
);
yield createRouterExtension.componentDataRef(Wrapper);
*factory({ Component }: { Component: ComponentType<PropsWithChildren<{}>> }) {
yield createRouterExtension.componentDataRef(Component);
},
});
@@ -30,15 +30,12 @@ export const SignInPageBlueprint = createExtensionBlueprint({
{
loader,
}: {
loader: (opts: {
config: typeof config;
inputs: typeof inputs;
}) => Promise<ComponentType<SignInPageProps>>;
loader: () => Promise<ComponentType<SignInPageProps>>;
},
{ config, inputs, node },
{ node },
) {
const ExtensionComponent = lazy(() =>
loader({ config, inputs }).then(component => ({ default: component })),
loader().then(component => ({ default: component })),
);
yield createSignInPageExtension.componentDataRef(props => (
@@ -312,9 +312,9 @@ class ExtensionBlueprintImpl<
>
>
> {
const optionsSchema =
typeof this.options.config?.schema === 'function'
? this.options.config?.schema(args.params!)
const optionsSchema = // can remove this args.params check with the split apart of .make
typeof this.options.config?.schema === 'function' && args.params
? this.options.config?.schema(args.params)
: this.options.config?.schema;
const schema = {
@@ -323,13 +323,13 @@ class ExtensionBlueprintImpl<
} as TConfigSchema & TExtensionConfigSchema;
const namespace =
typeof this.options.namespace === 'function'
? this.options.namespace(args.params!)
typeof this.options.namespace === 'function' && args.params
? this.options.namespace(args.params)
: this.options.namespace;
const name =
typeof this.options.name === 'function'
? this.options.name(args.params!)
typeof this.options.name === 'function' && args.params
? this.options.name(args.params)
: this.options.name;
return createExtension({