chore: getting somewhere with portable schemas and zod types

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2024-07-24 11:25:11 +02:00
parent 8e387301a4
commit d22f1316c5
3 changed files with 75 additions and 137 deletions
@@ -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);
}
+4 -63
View File
@@ -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)
```