chore: getting somewhere with portable schemas and zod types
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { AppNode } from '../apis';
|
||||
import { PortableSchema } from '../schema';
|
||||
import { PortableSchema, createSchemaFromZod } from '../schema';
|
||||
import { Expand } from '../types';
|
||||
import { ExtensionDataRef } from './createExtensionDataRef';
|
||||
import { ExtensionInput } from './createExtensionInput';
|
||||
@@ -106,10 +106,7 @@ export interface CreateExtensionOptions<
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionDefinition<
|
||||
TConfig,
|
||||
TConfigSchema = never /* TODO -> refactor */,
|
||||
> {
|
||||
export interface ExtensionDefinition<TConfig> {
|
||||
$$type: '@backstage/ExtensionDefinition';
|
||||
readonly kind?: string;
|
||||
readonly namespace?: string;
|
||||
@@ -117,9 +114,6 @@ export interface ExtensionDefinition<
|
||||
readonly attachTo: { id: string; input: string };
|
||||
readonly disabled: boolean;
|
||||
readonly configSchema?: PortableSchema<TConfig>;
|
||||
readonly config?: {
|
||||
schema: TConfigSchema;
|
||||
};
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -162,6 +156,18 @@ export function createExtension<
|
||||
>(
|
||||
options: CreateExtensionOptions<TOutput, TInputs, TConfig, TConfigSchema>,
|
||||
): ExtensionDefinition<TConfig> {
|
||||
// TODO: resovle configSchema from either configSchema or config.schema
|
||||
const newConfigSchema = options.config?.schema;
|
||||
const configSchema = newConfigSchema
|
||||
? createSchemaFromZod(innerZ =>
|
||||
innerZ.object(
|
||||
Object.fromEntries(
|
||||
Object.entries(newConfigSchema).map(([k, v]) => [k, v(innerZ)]),
|
||||
),
|
||||
),
|
||||
)
|
||||
: options.configSchema;
|
||||
|
||||
return {
|
||||
$$type: '@backstage/ExtensionDefinition',
|
||||
version: 'v1',
|
||||
@@ -172,8 +178,7 @@ export function createExtension<
|
||||
disabled: options.disabled ?? false,
|
||||
inputs: options.inputs ?? {},
|
||||
output: options.output,
|
||||
configSchema: options.configSchema,
|
||||
config: options.config,
|
||||
configSchema,
|
||||
factory({ inputs, ...rest }) {
|
||||
// TODO: Simplify this, but TS wouldn't infer the input type for some reason
|
||||
return options.factory({
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface CreateExtensionBlueprintOptions<
|
||||
TParams,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfigSchema extends { [key: string]: z.ZodTypeAny },
|
||||
TConfigSchema extends { [key in string]: z.ZodTypeAny },
|
||||
TDataRefs extends AnyExtensionDataMap,
|
||||
> {
|
||||
kind: string;
|
||||
@@ -51,9 +51,9 @@ export interface CreateExtensionBlueprintOptions<
|
||||
params: TParams,
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: Expand<{
|
||||
config: {
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
}>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
@@ -68,7 +68,7 @@ export interface ExtensionBlueprint<
|
||||
TParams,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfigSchema extends { [key: string]: z.ZodTypeAny },
|
||||
TConfig extends { [key in string]: unknown },
|
||||
TDataRefs extends AnyExtensionDataMap,
|
||||
> {
|
||||
dataRefs: TDataRefs;
|
||||
@@ -79,7 +79,7 @@ export interface ExtensionBlueprint<
|
||||
* You must either pass `params` directly, or define a `factory` that can
|
||||
* optionally call the original factory with the same params.
|
||||
*/
|
||||
make<TExtensionConfigSchema extends { [key: string]: z.ZodTypeAny }>(
|
||||
make<TExtensionConfigSchema extends { [key in string]: z.ZodTypeAny }>(
|
||||
args: {
|
||||
namespace?: string;
|
||||
name?: string;
|
||||
@@ -100,23 +100,17 @@ export interface ExtensionBlueprint<
|
||||
originalFactory: (
|
||||
params: TParams,
|
||||
context?: {
|
||||
config?: Expand<{
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
}>;
|
||||
config?: TConfig;
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
) => Expand<ExtensionDataValues<TOutput>>,
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: Expand<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
}
|
||||
>;
|
||||
config: {
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
} & TConfig;
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
@@ -130,8 +124,7 @@ export interface ExtensionBlueprint<
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
},
|
||||
TExtensionConfigSchema
|
||||
} & TConfig
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -142,7 +135,7 @@ class ExtensionBlueprintImpl<
|
||||
TParams,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfigSchema extends { [key: string]: z.ZodTypeAny },
|
||||
TConfigSchema extends { [key in string]: z.ZodTypeAny },
|
||||
TDataRefs extends AnyExtensionDataMap,
|
||||
> {
|
||||
constructor(
|
||||
@@ -160,7 +153,7 @@ class ExtensionBlueprintImpl<
|
||||
dataRefs: TDataRefs;
|
||||
|
||||
public make<
|
||||
TExtensionConfigSchema extends { [key: string]: z.ZodTypeAny },
|
||||
TExtensionConfigSchema extends { [key in string]: z.ZodTypeAny },
|
||||
>(args: {
|
||||
namespace?: string;
|
||||
name?: string;
|
||||
@@ -171,45 +164,53 @@ class ExtensionBlueprintImpl<
|
||||
params?: TParams;
|
||||
config?: {
|
||||
schema: {
|
||||
[key in keyof TConfigSchema]: (zImpl: typeof z) => TConfigSchema[key];
|
||||
[key in keyof TExtensionConfigSchema]: (
|
||||
zImpl: typeof z,
|
||||
) => TExtensionConfigSchema[key];
|
||||
};
|
||||
};
|
||||
factory?(
|
||||
originalFactory: (
|
||||
params: TParams,
|
||||
context?: {
|
||||
config?: Expand<{
|
||||
config?: {
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
}>;
|
||||
};
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
) => Expand<ExtensionDataValues<TOutput>>,
|
||||
context: {
|
||||
node: AppNode;
|
||||
config: Expand<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
}
|
||||
>;
|
||||
config: {
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
};
|
||||
inputs: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
): Expand<ExtensionDataValues<TOutput>>;
|
||||
}): ExtensionDefinition<
|
||||
Expand<
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
}
|
||||
>,
|
||||
Expand<TConfigSchema & TExtensionConfigSchema>
|
||||
{
|
||||
[key in keyof TExtensionConfigSchema]: z.infer<
|
||||
TExtensionConfigSchema[key]
|
||||
>;
|
||||
} & {
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
}
|
||||
> {
|
||||
const schema = {
|
||||
...this.options.config?.schema,
|
||||
...args.config,
|
||||
} as {
|
||||
[key in keyof TConfigSchema]: (zImpl: typeof z) => TConfigSchema[key];
|
||||
} & {
|
||||
[key in keyof TExtensionConfigSchema]: (
|
||||
zImpl: typeof z,
|
||||
) => TExtensionConfigSchema[key];
|
||||
};
|
||||
|
||||
return createExtension({
|
||||
kind: this.options.kind,
|
||||
namespace: args.namespace ?? this.options.namespace,
|
||||
@@ -218,31 +219,16 @@ class ExtensionBlueprintImpl<
|
||||
disabled: args.disabled ?? this.options.disabled,
|
||||
inputs: args.inputs ?? this.options.inputs,
|
||||
output: args.output ?? this.options.output,
|
||||
config: {
|
||||
schema: {
|
||||
...this.options.config?.schema,
|
||||
...args.config,
|
||||
} as Expand<
|
||||
{
|
||||
[key in keyof TConfigSchema]: (
|
||||
zImpl: typeof z,
|
||||
) => TConfigSchema[key];
|
||||
} & {
|
||||
[key in keyof TExtensionConfigSchema]: (
|
||||
zImpl: typeof z,
|
||||
) => TExtensionConfigSchema[key];
|
||||
}
|
||||
>,
|
||||
},
|
||||
config: Object.keys(schema).length === 0 ? undefined : { schema },
|
||||
factory: ({ node, config, inputs }) => {
|
||||
if (args.factory) {
|
||||
return args.factory(
|
||||
(
|
||||
innerParams: TParams,
|
||||
innerContext?: {
|
||||
config?: Expand<{
|
||||
config?: {
|
||||
[key in keyof TConfigSchema]: z.infer<TConfigSchema[key]>;
|
||||
}>;
|
||||
};
|
||||
inputs?: Expand<ResolvedExtensionInputs<TInputs>>;
|
||||
},
|
||||
) =>
|
||||
@@ -280,7 +266,7 @@ export function createExtensionBlueprint<
|
||||
TParams,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
TOutput extends AnyExtensionDataMap,
|
||||
TConfigSchema extends { [key: string]: z.ZodTypeAny },
|
||||
TConfigSchema extends { [key in string]: z.ZodTypeAny },
|
||||
TDataRefs extends AnyExtensionDataMap = never,
|
||||
>(
|
||||
options: CreateExtensionBlueprintOptions<
|
||||
@@ -290,6 +276,12 @@ export function createExtensionBlueprint<
|
||||
TConfigSchema,
|
||||
TDataRefs
|
||||
>,
|
||||
): ExtensionBlueprint<TParams, TInputs, TOutput, TConfigSchema, TDataRefs> {
|
||||
return new ExtensionBlueprintImpl(options) as any;
|
||||
): ExtensionBlueprint<
|
||||
TParams,
|
||||
TInputs,
|
||||
TOutput,
|
||||
{ [key in keyof TConfigSchema]: z.infer<TConfigSchema[key]> },
|
||||
TDataRefs
|
||||
> {
|
||||
return new ExtensionBlueprintImpl(options);
|
||||
}
|
||||
|
||||
@@ -6,72 +6,17 @@
|
||||
import { BackstagePlugin } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { objectUtil } from 'zod';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { ZodArray } from 'zod';
|
||||
import { ZodBoolean } from 'zod';
|
||||
import { ZodEnum } from 'zod';
|
||||
import { ZodNativeEnum } from 'zod';
|
||||
import { ZodNumber } from 'zod';
|
||||
import { ZodOptional } from 'zod';
|
||||
import { ZodString } from 'zod';
|
||||
import { ZodTuple } from 'zod';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "CatalogGraphEntityCard" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const CatalogGraphEntityCard: ExtensionDefinition<
|
||||
{
|
||||
filter?: string | undefined;
|
||||
height?: number | undefined;
|
||||
curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined;
|
||||
direction?: Direction | undefined;
|
||||
title?: string | undefined;
|
||||
zoom?: 'disabled' | 'enabled' | 'enable-on-click' | undefined;
|
||||
relations?: string[] | undefined;
|
||||
maxDepth?: number | undefined;
|
||||
kinds?: string[] | undefined;
|
||||
unidirectional?: boolean | undefined;
|
||||
mergeRelations?: boolean | undefined;
|
||||
relationPairs?: [string, string][] | undefined;
|
||||
},
|
||||
objectUtil.extendShape<
|
||||
objectUtil.extendShape<
|
||||
{
|
||||
filter: ZodOptional<ZodString>;
|
||||
},
|
||||
{
|
||||
title: ZodOptional<ZodString>;
|
||||
height: ZodOptional<ZodNumber>;
|
||||
}
|
||||
>,
|
||||
{
|
||||
kinds: ZodOptional<ZodArray<ZodString, 'many'>>;
|
||||
relations: ZodOptional<ZodArray<ZodString, 'many'>>;
|
||||
maxDepth: ZodOptional<ZodNumber>;
|
||||
unidirectional: ZodOptional<ZodBoolean>;
|
||||
mergeRelations: ZodOptional<ZodBoolean>;
|
||||
direction: ZodOptional<ZodNativeEnum<typeof Direction>>;
|
||||
relationPairs: ZodOptional<
|
||||
ZodArray<ZodTuple<[ZodString, ZodString], null>, 'many'>
|
||||
>;
|
||||
zoom: ZodOptional<ZodEnum<['enabled', 'disabled', 'enable-on-click']>>;
|
||||
curve: ZodOptional<ZodEnum<['curveStepBefore', 'curveMonotoneX']>>;
|
||||
}
|
||||
>,
|
||||
{
|
||||
filter?: string | undefined;
|
||||
height?: number | undefined;
|
||||
curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined;
|
||||
direction?: Direction | undefined;
|
||||
title?: string | undefined;
|
||||
zoom?: 'disabled' | 'enabled' | 'enable-on-click' | undefined;
|
||||
relations?: string[] | undefined;
|
||||
maxDepth?: number | undefined;
|
||||
kinds?: string[] | undefined;
|
||||
unidirectional?: boolean | undefined;
|
||||
mergeRelations?: boolean | undefined;
|
||||
relationPairs?: [string, string][] | undefined;
|
||||
title: string | undefined;
|
||||
height: number | undefined;
|
||||
} & {
|
||||
filter: string | undefined;
|
||||
}
|
||||
>;
|
||||
|
||||
@@ -93,9 +38,5 @@ const _default: BackstagePlugin<
|
||||
>;
|
||||
export default _default;
|
||||
|
||||
// Warnings were encountered during analysis:
|
||||
//
|
||||
// src/alpha.d.ts:6:5 - (ae-forgotten-export) The symbol "Direction" needs to be exported by the entry point alpha.d.ts
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user