diff --git a/src/entity/Tomato.ts b/src/entity/Tomato.ts new file mode 100644 index 0000000..966b128 --- /dev/null +++ b/src/entity/Tomato.ts @@ -0,0 +1,16 @@ +import { Field, ID, ObjectType } from "type-graphql"; +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm"; +import { autoQueries } from "../modules/dynamic/AutoDecorators"; +import { findAll, findOne } from "../modules/dynamic/MyQueries"; + +@autoQueries([findAll, findOne]) +@ObjectType() +@Entity() +export class Tomato extends BaseEntity { + @Field(() => ID) + @PrimaryGeneratedColumn() + id: number; + + @Column() + size: number; +} diff --git a/src/index.ts b/src/index.ts index 103a8e7..c348365 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,18 +1,19 @@ -import "reflect-metadata"; import { ApolloServer } from "apollo-server-express"; +import connectRedis from "connect-redis"; +import cors from "cors"; import Express from "express"; +import session from "express-session"; +import "reflect-metadata"; import { formatArgumentValidationError } from "type-graphql"; import { createConnection } from "typeorm"; -import session from "express-session"; -import connectRedis from "connect-redis"; -import cors from "cors"; - import { redis } from "./redis"; +// @ts-ignore import { createSchema } from "./utils/createSchema"; const main = async () => { await createConnection(); + // @ts-ignore const schema = await createSchema(); const apolloServer = new ApolloServer({ diff --git a/src/modules/dynamic/AutoDecorators.ts b/src/modules/dynamic/AutoDecorators.ts new file mode 100644 index 0000000..09cb1b6 --- /dev/null +++ b/src/modules/dynamic/AutoDecorators.ts @@ -0,0 +1,18 @@ +import { Query } from "type-graphql"; +import { newR } from "./DynamicResolvers"; +import { createResolver } from "./ResolversFactory"; + +// @ts-ignore +export function autoQueries(queries: any[]): any { + return function(target: any) { + const resolverClass = createResolver( + target.name, + target, + class {}, + target, + [{ genre: Query, meth: queries }] + ); + + newR.resolvers = [resolverClass]; + }; +} diff --git a/src/modules/dynamic/DynamicResolvers.ts b/src/modules/dynamic/DynamicResolvers.ts new file mode 100644 index 0000000..88c303f --- /dev/null +++ b/src/modules/dynamic/DynamicResolvers.ts @@ -0,0 +1,15 @@ +class DynamicResolvers { + private _resolvers: any[] = []; + + get resolvers(): any[] { + return this._resolvers; + } + + set resolvers(resolver: any[]) { + this._resolvers.push(...resolver); + } + + empty() {} +} + +export const newR = new DynamicResolvers(); diff --git a/src/modules/dynamic/MyMutations.ts b/src/modules/dynamic/MyMutations.ts new file mode 100644 index 0000000..0fbff18 --- /dev/null +++ b/src/modules/dynamic/MyMutations.ts @@ -0,0 +1,6 @@ +export const createM = (data: any) => { + console.log("mutation create data: ", data); +}; +export const deleteM = (data: any) => { + console.log("mutation delete data: ", data); +}; diff --git a/src/modules/dynamic/MyQueries.ts b/src/modules/dynamic/MyQueries.ts new file mode 100644 index 0000000..d2b0833 --- /dev/null +++ b/src/modules/dynamic/MyQueries.ts @@ -0,0 +1,6 @@ +export function findAll(data: any) { + console.log("query findAll data: ", data); +} +export function findOne(data: any) { + console.log("query findOne data: ", data); +} diff --git a/src/modules/dynamic/ResolversFactory.ts b/src/modules/dynamic/ResolversFactory.ts new file mode 100644 index 0000000..cb38c0c --- /dev/null +++ b/src/modules/dynamic/ResolversFactory.ts @@ -0,0 +1,82 @@ +import { ClassType, Query, Resolver } from "type-graphql"; + +// @ts-ignore +export function createResolver( + suffix: string, + returnType: T, + // @ts-ignore + inputType: X, + // @ts-ignore + entity: any, + operations: any[] +): any { + function classDecorator(): any { + console.log("classDecorator operations: ", operations); + return function( + target: any, + // @ts-ignore + propertyKey: string, + // @ts-ignore + descriptor: PropertyDescriptor + ) { + operations.forEach(oper => { + // @ts-ignore + const deco = oper.genre; + console.log("classDecorator deco: ", deco); + // @ts-ignore + oper.meth.forEach(method => { + target.prototype[method.name] = method; + const targetPrototype = target.prototype; + const methodKey = method.name; + // @ts-ignore + deco(() => returnType, { + name: `${method.name}${suffix}`, + nullable: true + })( + targetPrototype, + // @ts-ignore + methodKey + ); + console.log("classDecorator targetPrototype: ", targetPrototype); + }); + }); + /* console.log("target: ", target); + console.log("propertyKey: ", propertyKey); + console.log("descriptor: ", descriptor); */ + }; + } + + @classDecorator() + @Resolver() + class ResolversFactory { + @Query(() => String, { + nullable: true + }) + base() { + return console.log("base query"); + } + } + + return ResolversFactory; +} + +/* + * List of type-graphql decorators and their needed parameters: + * ParameterDecorator: prototype, propertyKey, parameterIndex + * Arg, Args, Ctx, Info, PubSub, Root, + * + * ClassDecorator: target + * ArgsType, InputType, InterfaceType, ObjectType, Resolver, + * + * MethodAndPropDecorator: prototype, propertyKey, descriptor + * Authorized, Field, + * + * MethodDecorator: prototype, propertyKey/methodName + * FieldResolver, Mutation, Query, Subscription, + * + * UseMiddleware can be: MethodAndPropDecorator, MethodDecorator , PropertyDecorator + * + * registerEnumType accepts enumObj and EnumConfig + * createUnionType to be seen better + * + */ diff --git a/src/modules/user/CreateUser.ts b/src/modules/user/CreateUser.ts index f431243..6f08d85 100644 --- a/src/modules/user/CreateUser.ts +++ b/src/modules/user/CreateUser.ts @@ -1,16 +1,16 @@ import { - Resolver, - Mutation, Arg, ClassType, - InputType, Field, + InputType, + Mutation, + Resolver, UseMiddleware } from "type-graphql"; -import { RegisterInput } from "./register/RegisterInput"; -import { User } from "../../entity/User"; -import { Product } from "../../entity/Product"; import { Middleware } from "type-graphql/interfaces/Middleware"; +import { Product } from "../../entity/Product"; +import { User } from "../../entity/User"; +import { RegisterInput } from "./register/RegisterInput"; function createResolver( suffix: string, diff --git a/src/modules/user/Login.ts b/src/modules/user/Login.ts index 02aa53f..4c82dce 100644 --- a/src/modules/user/Login.ts +++ b/src/modules/user/Login.ts @@ -1,6 +1,5 @@ -import { Resolver, Mutation, Arg, Ctx } from "type-graphql"; import bcrypt from "bcryptjs"; - +import { Arg, Ctx, Mutation, Resolver } from "type-graphql"; import { User } from "../../entity/User"; import { MyContext } from "../../types/MyContext"; diff --git a/src/utils/createSchema.ts b/src/utils/createSchema.ts index 39a7768..6988187 100644 --- a/src/utils/createSchema.ts +++ b/src/utils/createSchema.ts @@ -1,30 +1,55 @@ import { buildSchema } from "type-graphql"; +import { newR } from "../modules/dynamic/DynamicResolvers"; import { ChangePasswordResolver } from "../modules/user/ChangePassword"; import { ConfirmUserResolver } from "../modules/user/ConfirmUser"; +import { + CreateProductResolver, + CreateUserResolver +} from "../modules/user/CreateUser"; import { ForgotPasswordResolver } from "../modules/user/ForgotPassword"; import { LoginResolver } from "../modules/user/Login"; import { LogoutResolver } from "../modules/user/Logout"; import { MeResolver } from "../modules/user/Me"; import { RegisterResolver } from "../modules/user/Register"; -import { - CreateUserResolver, - CreateProductResolver -} from "../modules/user/CreateUser"; -export const createSchema = () => - buildSchema({ - resolvers: [ - ChangePasswordResolver, - ConfirmUserResolver, - ForgotPasswordResolver, - LoginResolver, - LogoutResolver, - MeResolver, - RegisterResolver, - CreateUserResolver, - CreateProductResolver - ], - authChecker: ({ context: { req } }) => { - return !!req.session.userId; - } - }); +let createSchema; +const init = async () => { + // quick and dirty loader. Put here to secure correct call order of the decorators + // but Not sure if needed. needs test + // https://stackoverflow.com/questions/5364928/node-js-require-all-files-in-a-folder + var normalizedPath = require("path").join(__dirname, "../entity"); + + const fs = require("fs"); + await fs + .readdirSync(normalizedPath) + //@ts-ignore + .forEach(function(file) { + import("../entity/" + file); + }); + //@ts-ignore + const dynaResolver = newR.resolvers; + //@ts-ignore + const allResolvers = [ + ChangePasswordResolver, + ConfirmUserResolver, + ForgotPasswordResolver, + LoginResolver, + LogoutResolver, + MeResolver, + RegisterResolver, + CreateUserResolver, + CreateProductResolver + ]; + allResolvers.push(...dynaResolver); + + createSchema = () => + buildSchema({ + resolvers: allResolvers, + authChecker: ({ context: { req } }) => { + return !!req.session.userId; + } + }); +}; +init(); + +export { createSchema };