@zuu/owl
Version:
Zuu's Experimental GraphQL Implementation
359 lines • 17.8 kB
JavaScript
;
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