UNPKG

@zuu/owl

Version:

Zuu's Experimental GraphQL Implementation

359 lines 17.8 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const graphql_1 = require("graphql"); const graphql_subscriptions_1 = require("graphql-subscriptions"); const getMetadataStorage_1 = require("../metadata/getMetadataStorage"); const types_1 = require("../helpers/types"); const create_1 = require("../resolvers/create"); const build_context_1 = require("./build-context"); const errors_1 = require("../errors"); const utils_1 = require("./utils"); class SchemaGenerator { static generateFromMetadata(options) { return __awaiter(this, void 0, void 0, function* () { const schema = this.generateFromMetadataSync(options); const { errors } = yield graphql_1.graphql(schema, graphql_1.introspectionQuery); if (errors) { throw new errors_1.GeneratingSchemaError(errors); } return schema; }); } static generateFromMetadataSync(options) { this.checkForErrors(options); build_context_1.BuildContext.create(options); getMetadataStorage_1.getMetadataStorage().build(); this.buildTypesInfo(); const schema = new graphql_1.GraphQLSchema({ query: this.buildRootQueryType(), mutation: this.buildRootMutationType(), subscription: this.buildRootSubscriptionType(), types: this.buildOtherTypes(), }); build_context_1.BuildContext.reset(); return schema; } static checkForErrors(options) { if (getMetadataStorage_1.getMetadataStorage().authorizedFields.length !== 0 && options.authChecker === undefined) { throw new Error("You need to provide `authChecker` function for `@Authorized` decorator usage!"); } } static buildTypesInfo() { this.unionTypesInfo = getMetadataStorage_1.getMetadataStorage().unions.map(unionMetadata => { return { unionSymbol: unionMetadata.symbol, type: new graphql_1.GraphQLUnionType({ name: unionMetadata.name, description: unionMetadata.description, types: () => unionMetadata.types.map(objectType => this.objectTypesInfo.find(type => type.target === objectType).type), resolveType: instance => { const instanceTarget = unionMetadata.types.find(type => instance instanceof type); if (!instanceTarget) { throw new errors_1.UnionResolveTypeError(unionMetadata); } return this.objectTypesInfo.find(type => type.target === instanceTarget).type; }, }), }; }); this.enumTypesInfo = getMetadataStorage_1.getMetadataStorage().enums.map(enumMetadata => { const enumMap = types_1.getEnumValuesMap(enumMetadata.enumObj); return { enumObj: enumMetadata.enumObj, type: new graphql_1.GraphQLEnumType({ name: enumMetadata.name, description: enumMetadata.description, values: Object.keys(enumMap).reduce((enumConfig, enumKey) => { enumConfig[enumKey] = { value: enumMap[enumKey], }; return enumConfig; }, {}), }), }; }); this.interfaceTypesInfo = getMetadataStorage_1.getMetadataStorage().interfaceTypes.map(interfaceType => { const interfaceSuperClass = Object.getPrototypeOf(interfaceType.target); const hasExtended = interfaceSuperClass.prototype !== undefined; const getSuperClassType = () => { const superClassTypeInfo = this.interfaceTypesInfo.find(type => type.target === interfaceSuperClass); return superClassTypeInfo ? superClassTypeInfo.type : undefined; }; return { target: interfaceType.target, type: new graphql_1.GraphQLInterfaceType({ name: interfaceType.name, description: interfaceType.description, fields: () => { let fields = interfaceType.fields.reduce((fieldsMap, field) => { fieldsMap[field.schemaName] = { description: field.description, type: this.getGraphQLOutputType(field.name, field.getType(), field.typeOptions), }; return fieldsMap; }, {}); if (hasExtended) { const superClass = getSuperClassType(); if (superClass) { const superClassFields = utils_1.getFieldMetadataFromObjectType(superClass); fields = Object.assign({}, superClassFields, fields); } } return fields; }, }), }; }); this.objectTypesInfo = getMetadataStorage_1.getMetadataStorage().objectTypes.map(objectType => { const objectSuperClass = Object.getPrototypeOf(objectType.target); const hasExtended = objectSuperClass.prototype !== undefined; const getSuperClassType = () => { const superClassTypeInfo = this.objectTypesInfo.find(type => type.target === objectSuperClass); return superClassTypeInfo ? superClassTypeInfo.type : undefined; }; const interfaceClasses = objectType.interfaceClasses || []; return { target: objectType.target, type: new graphql_1.GraphQLObjectType({ name: objectType.name, description: objectType.description, isTypeOf: hasExtended || interfaceClasses.length > 0 ? instance => instance instanceof objectType.target : undefined, interfaces: () => { let interfaces = interfaceClasses.map(interfaceClass => this.interfaceTypesInfo.find(info => info.target === interfaceClass).type); if (hasExtended) { const superClass = getSuperClassType(); if (superClass) { const superInterfaces = superClass.getInterfaces(); interfaces = Array.from(new Set(interfaces.concat(superInterfaces))); } } return interfaces; }, fields: () => { let fields = objectType.fields.reduce((fieldsMap, field) => { const fieldResolverMetadata = getMetadataStorage_1.getMetadataStorage().fieldResolvers.find(resolver => resolver.getObjectType() === objectType.target && resolver.methodName === field.name && (resolver.resolverClassMetadata === undefined || resolver.resolverClassMetadata.isAbstract === false)); fieldsMap[field.schemaName] = { type: this.getGraphQLOutputType(field.name, field.getType(), field.typeOptions), args: this.generateHandlerArgs(field.params), resolve: fieldResolverMetadata ? create_1.createAdvancedFieldResolver(fieldResolverMetadata) : create_1.createSimpleFieldResolver(field), description: field.description, deprecationReason: field.deprecationReason, }; return fieldsMap; }, {}); if (hasExtended) { const superClass = getSuperClassType(); if (superClass) { const superClassFields = utils_1.getFieldMetadataFromObjectType(superClass); fields = Object.assign({}, superClassFields, fields); } } if (objectType.interfaceClasses) { const interfacesFields = objectType.interfaceClasses.reduce((fieldsMap, interfaceClass) => { const interfaceType = this.interfaceTypesInfo.find(type => type.target === interfaceClass).type; return Object.assign(fieldsMap, utils_1.getFieldMetadataFromObjectType(interfaceType)); }, {}); fields = Object.assign({}, interfacesFields, fields); } return fields; }, }), }; }); this.inputTypesInfo = getMetadataStorage_1.getMetadataStorage().inputTypes.map(inputType => { const objectSuperClass = Object.getPrototypeOf(inputType.target); const getSuperClassType = () => { const superClassTypeInfo = this.inputTypesInfo.find(type => type.target === objectSuperClass); return superClassTypeInfo ? superClassTypeInfo.type : undefined; }; return { target: inputType.target, type: new graphql_1.GraphQLInputObjectType({ name: inputType.name, description: inputType.description, fields: () => { let fields = inputType.fields.reduce((fieldsMap, field) => { fieldsMap[field.schemaName] = { description: field.description, type: this.getGraphQLInputType(field.name, field.getType(), field.typeOptions), }; return fieldsMap; }, {}); if (objectSuperClass.prototype !== undefined) { const superClass = getSuperClassType(); if (superClass) { const superClassFields = utils_1.getFieldMetadataFromInputType(superClass); fields = Object.assign({}, superClassFields, fields); } } return fields; }, }), }; }); } static buildRootQueryType() { return new graphql_1.GraphQLObjectType({ name: "Query", fields: this.generateHandlerFields(getMetadataStorage_1.getMetadataStorage().queries), }); } static buildRootMutationType() { if (getMetadataStorage_1.getMetadataStorage().mutations.length === 0) { return; } return new graphql_1.GraphQLObjectType({ name: "Mutation", fields: this.generateHandlerFields(getMetadataStorage_1.getMetadataStorage().mutations), }); } static buildRootSubscriptionType() { if (getMetadataStorage_1.getMetadataStorage().subscriptions.length === 0) { return; } return new graphql_1.GraphQLObjectType({ name: "Subscription", fields: this.generateSubscriptionsFields(getMetadataStorage_1.getMetadataStorage().subscriptions), }); } static buildOtherTypes() { return [ ...this.objectTypesInfo.map(it => it.type), ...this.interfaceTypesInfo.map(it => it.type), ]; } static generateHandlerFields(handlers) { return handlers.reduce((fields, handler) => { if (handler.resolverClassMetadata && handler.resolverClassMetadata.isAbstract) { return fields; } fields[handler.schemaName] = { type: this.getGraphQLOutputType(handler.methodName, handler.getReturnType(), handler.returnTypeOptions), args: this.generateHandlerArgs(handler.params), resolve: create_1.createHandlerResolver(handler), description: handler.description, deprecationReason: handler.deprecationReason, }; return fields; }, {}); } static generateSubscriptionsFields(subscriptionsHandlers) { const { pubSub } = build_context_1.BuildContext; const basicFields = this.generateHandlerFields(subscriptionsHandlers); return subscriptionsHandlers.reduce((fields, handler) => { if (handler.resolverClassMetadata && handler.resolverClassMetadata.isAbstract) { return fields; } fields[handler.schemaName].subscribe = handler.filter ? graphql_subscriptions_1.withFilter(() => pubSub.asyncIterator(handler.topics), (payload, args, context, info) => { const resolverFilterData = { payload, args, context, info }; return handler.filter(resolverFilterData); }) : () => pubSub.asyncIterator(handler.topics); return fields; }, basicFields); } static generateHandlerArgs(params) { return params.reduce((args, param) => { if (param.kind === "arg") { args[param.name] = { description: param.description, type: this.getGraphQLInputType(param.name, param.getType(), param.typeOptions), }; } else if (param.kind === "args") { const argumentType = getMetadataStorage_1.getMetadataStorage().argumentTypes.find(it => it.target === param.getType()); let superClass = Object.getPrototypeOf(argumentType.target); while (superClass.prototype !== undefined) { const superArgumentType = getMetadataStorage_1.getMetadataStorage().argumentTypes.find(it => it.target === superClass); this.mapArgFields(superArgumentType, args); superClass = Object.getPrototypeOf(superClass); } this.mapArgFields(argumentType, args); } return args; }, {}); } static mapArgFields(argumentType, args = {}) { argumentType.fields.forEach(field => { args[field.schemaName] = { description: field.description, type: this.getGraphQLInputType(field.name, field.getType(), field.typeOptions), }; }); } static getGraphQLOutputType(typeOwnerName, type, typeOptions = {}) { let gqlType; gqlType = types_1.convertTypeIfScalar(type); if (!gqlType) { const objectType = this.objectTypesInfo.find(it => it.target === type); if (objectType) { gqlType = objectType.type; } } if (!gqlType) { const interfaceType = this.interfaceTypesInfo.find(it => it.target === type); if (interfaceType) { gqlType = interfaceType.type; } } if (!gqlType) { const enumType = this.enumTypesInfo.find(it => it.enumObj === type); if (enumType) { gqlType = enumType.type; } } if (!gqlType) { const unionType = this.unionTypesInfo.find(it => it.unionSymbol === type); if (unionType) { gqlType = unionType.type; } } if (!gqlType) { throw new Error(`Cannot determine GraphQL output type for ${typeOwnerName}`); } return types_1.wrapWithTypeOptions(gqlType, typeOptions); } static getGraphQLInputType(typeOwnerName, type, typeOptions = {}) { let gqlType; gqlType = types_1.convertTypeIfScalar(type); if (!gqlType) { const inputType = this.inputTypesInfo.find(it => it.target === type); if (inputType) { gqlType = inputType.type; } } if (!gqlType) { const enumType = this.enumTypesInfo.find(it => it.enumObj === type); if (enumType) { gqlType = enumType.type; } } if (!gqlType) { throw new Error(`Cannot determine GraphQL input type for ${typeOwnerName}`); } return types_1.wrapWithTypeOptions(gqlType, typeOptions); } } SchemaGenerator.objectTypesInfo = []; SchemaGenerator.inputTypesInfo = []; SchemaGenerator.interfaceTypesInfo = []; SchemaGenerator.enumTypesInfo = []; SchemaGenerator.unionTypesInfo = []; exports.SchemaGenerator = SchemaGenerator; //# sourceMappingURL=schema-generator.js.map