@cran/lib.gql.core
Version:
GQL Core Functionality
102 lines (80 loc) • 2.66 kB
text/typescript
import type { getDirective } from "@graphql-tools/utils";
import {
type GraphQLOutputType, type GraphQLSchema, isOutputType,
} from "graphql";
import { Plugin } from "./Plugin";
import {
type IntrospectionKind, IntrospectionType, type SchemaMapperArgs,
createDirective, getExtension, patchField, setExtension,
} from "../utilities";
export namespace withMetadata {
export type Fallback = (
value: Parameters<typeof getDirective>[1], name: string
) => unknown;
export type Fields = Record<IntrospectionKind | "default", Record<string, string>>;
export type Names = Partial<Record<IntrospectionKind, string>>;
export type Fallbacks = Partial<Record<IntrospectionKind, Fallback>>;
export interface Options {
fields : Fields;
names? : Names;
fallbacks?: Fallbacks;
}
}
export function withMetadata ( options: withMetadata.Options ) {
const plugins: Array<Plugin> = [ ];
for (const key in options.fields) {
if ("default" !== key) {
const kind = key as IntrospectionKind;
plugins.push(createDirective(
options.names?.[kind] ?? `${IntrospectionType[kind].name.slice(2)}Metadata`,
{ ...options.fields.default, ...options.fields[kind], },
{ [kind]: directiveMapper, }
));
}
}
plugins.push({
transformer: {
[Plugin.Phase.FINALIZE] ( schema ) {
for (const name in options.fields) {
if ("default" !== name) {
addFields(schema, name as IntrospectionKind, options);
}
}
return schema;
},
},
});
return plugins;
}
function directiveMapper (
[ directive, ]: Array<Record<string, unknown>>,
type: SchemaMapperArgs[0]
) {
setExtension(type, "metadata", directive);
}
function addFields (
schema: GraphQLSchema, kind: IntrospectionKind, options: withMetadata.Options
) {
const field = options.fields[kind];
for (const key in field) {
const type = schema.getType(field[key]!);
if (!type || !isOutputType(type)) {
throw new TypeError(`MetadataPlugin expects "GraphQLOutputType" received "${type?.name}"`);
}
addField(kind, key, type, options);
}
}
function addField (
kind: IntrospectionKind, key: string,
type: GraphQLOutputType, options: withMetadata.Options
) {
const fallback = options.fallbacks?.[kind] || alwaysNull;
patchField(IntrospectionType[kind], key, {
type,
resolve ( source: Parameters<withMetadata.Fallback>[0] ) {
const value = getExtension<Record<string, unknown>>(source, "metadata")?.[key];
return value ?? fallback(source, key);
},
});
}
function alwaysNull ( ): null { return null; }