UNPKG

@graphql-inspector/loaders

Version:
190 lines (181 loc) 7.55 kB
import { buildSchema, isInterfaceType, isObjectType, printSchema } from 'graphql'; import { loadDocuments, loadSchema, } from '@graphql-tools/load'; export class LoadersRegistry { constructor() { this.loaders = []; } register(loader) { this.loaders.push(loader); } registerModule(loaderName) { try { const loader = loadModule(`@graphql-inspector/${loaderName}-loader`); this.register(loader); } catch (error) { console.error(error); throw new Error(`Couldn't load ${loaderName} loader`); } } async loadSchema(pointer, options = {}, enableApolloFederation, enableApolloFederationV2, enableAWS) { const schema = await enrichError(loadSchema(pointer, { loaders: this.loaders, ...options, ...(enableApolloFederationV2 ? { schemas: [ buildSchema(/* GraphQL */ ` scalar _Any union _Entity scalar FieldSet scalar link__Import scalar federation__ContextFieldValue scalar federation__Scope scalar federation__Policy type Query enum link__Purpose { SECURITY EXECUTION } type _Service { sdl: String! } extend type Query { _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } directive @external on FIELD_DEFINITION | OBJECT directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @key( fields: FieldSet! resolvable: Boolean = true ) repeatable on OBJECT | INTERFACE directive @link( url: String! as: String for: link__Purpose import: [link__Import] ) repeatable on SCHEMA directive @shareable repeatable on OBJECT | FIELD_DEFINITION directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @tag( name: String! ) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION directive @override(from: String!) on FIELD_DEFINITION directive @composeDirective(name: String!) repeatable on SCHEMA directive @interfaceObject on OBJECT directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @requiresScopes( scopes: [[federation__Scope!]!]! ) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @policy( policies: [[federation__Policy!]!]! ) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM directive @context(name: String!) repeatable on INTERFACE | OBJECT | UNION directive @fromContext( field: federation__ContextFieldValue ) on ARGUMENT_DEFINITION directive @extends on OBJECT | INTERFACE `), ], } : {}), ...(enableApolloFederation ? { schemas: [ buildSchema(/* GraphQL */ ` scalar _FieldSet directive @external on FIELD_DEFINITION directive @requires(fields: _FieldSet!) on FIELD_DEFINITION directive @provides(fields: _FieldSet!) on FIELD_DEFINITION directive @key(fields: _FieldSet!) on OBJECT | INTERFACE directive @extends on OBJECT | INTERFACE `), ], } : {}), ...(enableAWS ? { schemas: [ buildSchema(/* GraphQL */ ` scalar AWSDate scalar AWSTime scalar AWSDateTime scalar AWSTimestamp scalar AWSEmail scalar AWSJSON scalar AWSURL scalar AWSPhone scalar AWSIPAddress scalar BigInt scalar Double directive @aws_subscribe(mutations: [String!]!) on FIELD_DEFINITION directive @deprecated( reason: String ) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION | ENUM | ENUM_VALUE directive @aws_auth(cognito_groups: [String!]!) on FIELD_DEFINITION directive @aws_api_key on FIELD_DEFINITION | OBJECT directive @aws_iam on FIELD_DEFINITION | OBJECT directive @aws_oidc on FIELD_DEFINITION | OBJECT directive @aws_cognito_user_pools( cognito_groups: [String!] ) on FIELD_DEFINITION | OBJECT directive @aws_lambda on FIELD_DEFINITION | OBJECT `), ], } : {}), })); if (enableApolloFederationV2) { return this.buildEntityUnion(schema); } return schema; } loadDocuments(pointer, options = {}) { return enrichError(loadDocuments(pointer, { loaders: this.loaders, ...options, })); } buildEntityUnion(schema) { const entityTypes = []; const typeMap = schema.getTypeMap(); // Find all types with @key directive for (const type of Object.values(typeMap)) { if ((isObjectType(type) || isInterfaceType(type)) && type.astNode?.directives?.some(dir => dir.name.value === 'key')) { entityTypes.push(type.name); } } if (entityTypes.length === 0) { return schema; // No entities found } // Create new schema SDL with populated _Entity union const entityUnion = `union _Entity = ${entityTypes.join(' | ')}`; // Rebuild schema with proper _Entity union const schemaSDL = printSchema(schema).replace('union _Entity', entityUnion); return buildSchema(schemaSDL); } } export function useLoaders(config) { const loaders = new LoadersRegistry(); for (const loaderName of config.loaders) loaders.registerModule(loaderName); return loaders; } function loadModule(name) { const mod = require(name); return mod.default || mod; } /** * Adds `(source: <file-path>)` suffix to error message if source is available */ function enrichError(looksPromising) { return looksPromising.catch(error => { if (error.source?.name) { error.message = `${error.message} (source: ${error.source?.name})`; } return Promise.reject(error); }); }