From 8c5d119210427c954c32ae3560ca7da96cec57b8 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 11 Feb 2023 07:03:39 +0500 Subject: [PATCH 01/52] in progress support for filtering with assosications sequelize --- package.json | 4 +++- src/cronJobs/index.ts | 2 +- src/crud/index.ts | 2 +- src/database/database.ts | 2 +- src/database/mysql/mysql.ts | 2 +- src/devServer.ts | 25 ++++++++++++++++++++++--- src/graphql/index.ts | 3 ++- src/index.ts | 2 +- src/mailer/index.ts | 2 +- src/modules/modules.ts | 8 +++----- src/modules/modulesHelpers.ts | 2 +- src/types/modules.ts | 7 ++++++- src/utils/replaceFilterOperators.ts | 2 +- 13 files changed, 44 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 2d818619..ea304516 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,9 @@ "graphql-fields": "^2.0.3", "graphql-type-json": "^0.3.2", "handlebars": "^4.5.3", - "lodash": "^4.17.15", + "lodash.get": "^4.4.2", + "lodash.isplainobject": "^4.0.6", + "lodash.omit": "^4.5.0", "log-symbols": "^3.0.0", "moment": "^2.23.0", "morgan": "^1.9.1", diff --git a/src/cronJobs/index.ts b/src/cronJobs/index.ts index ade4ef8c..fa897c23 100644 --- a/src/cronJobs/index.ts +++ b/src/cronJobs/index.ts @@ -1,4 +1,4 @@ -import { get } from "lodash" +import get from "lodash.get" import nodeCron from "node-cron" import { useCronJobsProps } from "../types/cronJobs" import { iObject, WertikApp, WertikConfiguration } from "../types" diff --git a/src/crud/index.ts b/src/crud/index.ts index 35af8a33..1a35ebcc 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,4 +1,4 @@ -import { get } from "lodash" +import get from "lodash.get" import convertFiltersIntoSequalizeObject from "../utils/convertFiltersIntoSequalizeObject" export const paginate = async (arg, tableInstance) => { diff --git a/src/database/database.ts b/src/database/database.ts index 1294b664..623e8191 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,4 +1,4 @@ -import { get } from "lodash" +import get from "lodash.get" import { paginate } from "../crud/index" import { Store } from "../types" import { WertikApp } from "../types" diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 5df338f6..71bc104f 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -1,7 +1,7 @@ import { Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" import { useMysqlDatabaseProps } from "../../types/database" -import { get } from "lodash" +import get from "lodash.get" export const getAllRelationships = (dbName: String) => { return ` diff --git a/src/devServer.ts b/src/devServer.ts index 7c5566ce..d9c5e09a 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -4,9 +4,9 @@ wertik({ port: 1200, graphql: useGraphql(), database: { - wapgee: useMysqlDatabase({ + wapgee_prod_new: useMysqlDatabase({ port: 3306, - name: "wapgee", + name: "wapgee_prod_new", host: "localhost", password: "pass", username: "root", @@ -24,7 +24,26 @@ wertik({ name: "User", useDatabase: true, table: "users", - database: "wapgee", + database: "wapgee_prod_new", + on: function ({hasMany}) { + hasMany({ + database: "wapgee_prod_new", + graphqlKey: "posts", + module: "Post", + allowFiltering: true, + options: { + as: "posts", + foreignKey: "created_by", + sourceKey: "id", + } + }) + } + }), + Post: useModule({ + name: "Post", + useDatabase: true, + table: "post", + database: "wapgee_prod_new", }), test: useModule({ name: "Shirts", diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 4d5513e1..4ee88672 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -1,4 +1,5 @@ -import { get, omit } from "lodash" +import get from "lodash.get" +import omit from "lodash.omit" import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" import { useGraphqlProps, GraphqlInitializeProps } from "../types/graphql" diff --git a/src/index.ts b/src/index.ts index 75040953..834b3b71 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { get } from "lodash" +import get from "lodash.get" import express from "express" import store from "./store" import { diff --git a/src/mailer/index.ts b/src/mailer/index.ts index 49d5b1af..4257aa69 100644 --- a/src/mailer/index.ts +++ b/src/mailer/index.ts @@ -2,7 +2,7 @@ import nodemailer from "nodemailer" import handlebars from "handlebars" import { useMailerProps, WertikApp, WertikConfiguration } from "../types" import { SendEmailProps } from "../types/mailer" -import { get } from "lodash" +import get from "lodash.get" export const useMailer = (props: useMailerProps) => { return async () => { diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 94831b6b..a4e70aeb 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -1,4 +1,4 @@ -import { get } from "lodash" +import get from "lodash.get" import crud from "../crud" import { databaseDefaultOptions } from "../utils/defaultOptions" import { RelationParams, useModuleProps } from "../types/modules" @@ -57,6 +57,8 @@ export const useModule = (moduleProps: useModuleProps) => { }) => { let tableInstance: ModelCtor> let graphqlSchema = [] + let listSchema = "" + let filterSchema = [`input ${moduleProps.name}FilterInput {`] const useDatabase = get(moduleProps, "useDatabase", false) @@ -97,8 +99,6 @@ export const useModule = (moduleProps: useModuleProps) => { }, 2500) } - let listSchema = "" - let filterSchema = [] if (useDatabase) { var createSchema = [] var updateSchema = [] @@ -157,8 +157,6 @@ export const useModule = (moduleProps: useModuleProps) => { createSchema = getCreateSchema(moduleProps, tableInfo) - filterSchema = [`input ${moduleProps.name}FilterInput {`] - tableInfo.columns.forEach((column) => { let filterInput = column.databaseType.toLowerCase() === "enum" diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 324475f9..23a8d19f 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -1,4 +1,4 @@ -import { get } from "lodash" +import get from "lodash.get" import { useModuleProps } from "../types/modules" import { TableInfo } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" diff --git a/src/types/modules.ts b/src/types/modules.ts index 7040f55b..8ecd8d01 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -36,7 +36,12 @@ export interface RelationParams { database: string options?: { [key: string]: string | number | null - } + }; + /** + * Allow filtering with table as well. + * @default false + */ + allowFiltering?: boolean; } export type useSchemaProps = string diff --git a/src/utils/replaceFilterOperators.ts b/src/utils/replaceFilterOperators.ts index ab5aa382..7f092292 100644 --- a/src/utils/replaceFilterOperators.ts +++ b/src/utils/replaceFilterOperators.ts @@ -1,4 +1,4 @@ -import { isPlainObject } from "lodash" +import isPlainObject from "lodash.isplainobject" import sequelize from "sequelize" const Op = sequelize.Op From f3b1e133bd8a4192177d459e96f8f0437d58aa35 Mon Sep 17 00:00:00 2001 From: ilyas Date: Mon, 20 Feb 2023 07:37:30 +0500 Subject: [PATCH 02/52] Replace pagination proerties with just pagination --- .vscode/settings.json | 5 +++ src/crud/index.ts | 34 ++++++++++--------- src/graphql/generalSchema.ts | 6 ++++ src/index.ts | 2 +- src/modules/modules.ts | 32 +---------------- src/modules/modulesHelpers.ts | 33 ++++++++++++++++++ src/types/modules.ts | 2 +- ...s => convertFiltersIntoSequelizeObject.ts} | 4 +-- 8 files changed, 67 insertions(+), 51 deletions(-) create mode 100644 .vscode/settings.json rename src/utils/{convertFiltersIntoSequalizeObject.ts => convertFiltersIntoSequelizeObject.ts} (52%) diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..b6b0a3c6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "Wertik" + ] +} \ No newline at end of file diff --git a/src/crud/index.ts b/src/crud/index.ts index 1a35ebcc..af074abf 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,10 +1,10 @@ import get from "lodash.get" -import convertFiltersIntoSequalizeObject from "../utils/convertFiltersIntoSequalizeObject" +import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" export const paginate = async (arg, tableInstance) => { const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} const offset = limit * (page - 1) - const where = await convertFiltersIntoSequalizeObject(arg.where) + const where = await convertFiltersIntoSequelizeObject(arg.where) const { count, rows } = await tableInstance.findAndCountAll({ where, offset, @@ -12,17 +12,19 @@ export const paginate = async (arg, tableInstance) => { order: sorting.map(({ column, type }) => [column, type]), }) const totalPages = Math.ceil(count / limit) + const pagination = { + total: count, + nextPage: page + 1, + page, + previousPage: page === 1 ? 1 : page - 1, + pages: totalPages, + hasMore: page < totalPages, + limit, + } return { list: rows, - paginationProperties: { - total: count, - nextPage: page + 1, - page, - previousPage: page === 1 ? 1 : page - 1, - pages: totalPages, - hasMore: page < totalPages, - limit, - }, + paginationProperties: pagination, + pagination } } @@ -36,7 +38,7 @@ export default function (module, schemaInformation, store) { list: [${module.name}] pagination: Pagination sorting: Sorting - paginationProperties: PaginationProperties + paginationProperties: PaginationProperties @deprecated(reason: "Use pagination instead") } type ${module.name}BulkMutationResponse { returning: [${module.name}] @@ -112,7 +114,7 @@ export default function (module, schemaInformation, store) { function () {} )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args - const where = await convertFiltersIntoSequalizeObject( + const where = await convertFiltersIntoSequelizeObject( args.where ) const response = await schemaInformation.tableInstance.update( @@ -140,7 +142,7 @@ export default function (module, schemaInformation, store) { function () {} )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args - const where = await convertFiltersIntoSequalizeObject( + const where = await convertFiltersIntoSequelizeObject( args.where ) await schemaInformation.tableInstance.destroy({ @@ -183,7 +185,7 @@ export default function (module, schemaInformation, store) { function () {} )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args - const where = await convertFiltersIntoSequalizeObject( + const where = await convertFiltersIntoSequelizeObject( args.where ) const find = await schemaInformation.tableInstance.findOne({ @@ -215,7 +217,7 @@ export default function (module, schemaInformation, store) { function () {} )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args - const where = await convertFiltersIntoSequalizeObject( + const where = await convertFiltersIntoSequelizeObject( args.where ) const count = await schemaInformation.tableInstance.count({ diff --git a/src/graphql/generalSchema.ts b/src/graphql/generalSchema.ts index eee9866d..cd5a1664 100644 --- a/src/graphql/generalSchema.ts +++ b/src/graphql/generalSchema.ts @@ -71,7 +71,12 @@ export default ` value: String! } type Pagination { + total: Int + pages: Int page: Int + nextPage: Int + previousPage: Int + hasMore: Boolean limit: Int } type Filter { @@ -88,4 +93,5 @@ export default ` hasMore: Boolean limit: Int } + ` diff --git a/src/index.ts b/src/index.ts index 834b3b71..72e6b77e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -162,7 +162,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( expressApp.get("/w/info", function (req, res) { res.json({ message: "You are running wertik-js v3", - version: require("./../../package.json").version, + version: require(`${process.cwd()}/package.json`).version, }) }) diff --git a/src/modules/modules.ts b/src/modules/modules.ts index a4e70aeb..a17986c9 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -1,46 +1,16 @@ import get from "lodash.get" -import crud from "../crud" import { databaseDefaultOptions } from "../utils/defaultOptions" import { RelationParams, useModuleProps } from "../types/modules" import { getCreateSchema, getUpdateSchema, generateEnumTypeForGraphql, + generateGenerateGraphQLCrud, } from "./modulesHelpers" import { getMysqlTableInfo } from "../database/mysql/getTableInfo" import { Store, WertikApp, WertikConfiguration } from "./../types/index" import { ModelCtor, Model, ModelAttributes } from "sequelize/types" -const generateGenerateGraphQLCrud = (props, schemaInformation, store) => { - const { graphql } = crud(props, schemaInformation, store) - const resolvers = graphql.generateCrudResolvers() - - store.graphql.typeDefs = store.graphql.typeDefs.concat( - `\n ${schemaInformation.schema} - \n ${schemaInformation.inputSchema.filters} - \n ${schemaInformation.inputSchema.create} - \n ${schemaInformation.inputSchema.update} - ` - ) - - store.graphql.typeDefs = store.graphql.typeDefs.concat( - `\n ${graphql.generateQueriesCrudSchema()}` - ) - store.graphql.typeDefs = store.graphql.typeDefs.concat( - `\n ${graphql.generateMutationsCrudSchema()}` - ) - - store.graphql.resolvers.Query = { - ...store.graphql.resolvers.Query, - ...resolvers.Query, - } - - store.graphql.resolvers.Mutation = { - ...store.graphql.resolvers.Mutation, - ...resolvers.Mutation, - } -} - /** * Wertik js module * @param props see interface useModuleProps diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 23a8d19f..03495e47 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -2,6 +2,7 @@ import get from "lodash.get" import { useModuleProps } from "../types/modules" import { TableInfo } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" +import crud from "../crud" export const generateDataTypeFromDescribeTableColumnType = (Type: string) => { let length = Type.match(/[0-9]/g)?.join("") @@ -101,3 +102,35 @@ export const generateEnumTypeForGraphql = (column: TableInfo["columns"][0]) => { ${column.enumValues.join("\n")} }` } + + + +export const generateGenerateGraphQLCrud = (props, schemaInformation, store) => { + const { graphql } = crud(props, schemaInformation, store) + const resolvers = graphql.generateCrudResolvers() + + store.graphql.typeDefs = store.graphql.typeDefs.concat( + `\n ${schemaInformation.schema} + \n ${schemaInformation.inputSchema.filters} + \n ${schemaInformation.inputSchema.create} + \n ${schemaInformation.inputSchema.update} + ` + ) + + store.graphql.typeDefs = store.graphql.typeDefs.concat( + `\n ${graphql.generateQueriesCrudSchema()}` + ) + store.graphql.typeDefs = store.graphql.typeDefs.concat( + `\n ${graphql.generateMutationsCrudSchema()}` + ) + + store.graphql.resolvers.Query = { + ...store.graphql.resolvers.Query, + ...resolvers.Query, + } + + store.graphql.resolvers.Mutation = { + ...store.graphql.resolvers.Mutation, + ...resolvers.Mutation, + } +} \ No newline at end of file diff --git a/src/types/modules.ts b/src/types/modules.ts index 8ecd8d01..9c9db1e2 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -51,7 +51,7 @@ export interface useModuleProps { */ name: string /** - * Are you using a database connection to this module? If yes, you will need to provide a database and table. + * If your module requires a database connection, you will need to provide a database and table using the 'useDatabase' property. This property should be set to the name of the database and table that your module will use to perform its operations. */ useDatabase: boolean /** diff --git a/src/utils/convertFiltersIntoSequalizeObject.ts b/src/utils/convertFiltersIntoSequelizeObject.ts similarity index 52% rename from src/utils/convertFiltersIntoSequalizeObject.ts rename to src/utils/convertFiltersIntoSequelizeObject.ts index a2f9c502..a13c2d3a 100644 --- a/src/utils/convertFiltersIntoSequalizeObject.ts +++ b/src/utils/convertFiltersIntoSequelizeObject.ts @@ -1,7 +1,7 @@ import replaceFilterOperators from "./replaceFilterOperators" -const convertFiltersIntoSequalizeObject = (filters) => { +const convertFiltersIntoSequelizeObject = (filters) => { return filters ? replaceFilterOperators(filters) : {} } -export default async (filters) => convertFiltersIntoSequalizeObject(filters) +export default async (filters) => convertFiltersIntoSequelizeObject(filters) From 49c3617b923ee6ca4d01a5265d82f9d9d951b611 Mon Sep 17 00:00:00 2001 From: ilyas Date: Tue, 21 Feb 2023 08:07:33 +0500 Subject: [PATCH 03/52] Added eager loading to list query --- src/crud/index.ts | 39 ++--- src/crud/paginate.ts | 45 ++++++ src/database/database.ts | 7 +- src/devServer.ts | 1 - src/index.ts | 4 + src/modules/modules.ts | 141 +++++++++--------- src/modules/modulesHelpers.ts | 26 +++- src/store.ts | 24 ++- src/types/modules.ts | 9 +- .../convertFiltersIntoSequelizeObject.ts | 2 +- 10 files changed, 187 insertions(+), 111 deletions(-) create mode 100644 src/crud/paginate.ts diff --git a/src/crud/index.ts b/src/crud/index.ts index af074abf..118298c8 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,32 +1,8 @@ import get from "lodash.get" +import { getRelationalFieldsRequestedInQuery } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" - -export const paginate = async (arg, tableInstance) => { - const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} - const offset = limit * (page - 1) - const where = await convertFiltersIntoSequelizeObject(arg.where) - const { count, rows } = await tableInstance.findAndCountAll({ - where, - offset, - limit, - order: sorting.map(({ column, type }) => [column, type]), - }) - const totalPages = Math.ceil(count / limit) - const pagination = { - total: count, - nextPage: page + 1, - page, - previousPage: page === 1 ? 1 : page - 1, - pages: totalPages, - hasMore: page < totalPages, - limit, - } - return { - list: rows, - paginationProperties: pagination, - pagination - } -} +const graphqlFields = require("graphql-fields") +import { paginate } from "./paginate" export default function (module, schemaInformation, store) { return { @@ -204,7 +180,14 @@ export default function (module, schemaInformation, store) { function () {} )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args - return await paginate(args, schemaInformation.tableInstance) + return await paginate( + args, + schemaInformation.tableInstance, + getRelationalFieldsRequestedInQuery( + module, + graphqlFields(info).list + ) + ) } ), [`count${module.name}`]: get( diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts new file mode 100644 index 00000000..7ae0bf54 --- /dev/null +++ b/src/crud/paginate.ts @@ -0,0 +1,45 @@ +import store from "../store" +import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" +import get from "lodash.get" + +export const paginate = async (arg, tableInstance, include: any[] = []) => { + const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} + const offset = limit * (page - 1) + const where = convertFiltersIntoSequelizeObject(arg.where) + const includes = include.map((c) => { + const _where = convertFiltersIntoSequelizeObject( + get(arg, `where.${c.referencedModule}`, {}) + ) + delete where[c.referencedModule] + return { + model: store.database.models[c.referencedModule], + as: c.options.as, + where: _where, + required: false + } + }) + + const { count, rows } = await tableInstance.findAndCountAll({ + where, + offset, + limit, + order: sorting.map(({ column, type }) => [column, type]), + include: includes, + }) + + const totalPages = Math.ceil(count / limit) + const pagination = { + total: count, + nextPage: page + 1, + page, + previousPage: page === 1 ? 1 : page - 1, + pages: totalPages, + hasMore: page < totalPages, + limit, + } + return { + list: rows, + paginationProperties: pagination, + pagination, + } +} diff --git a/src/database/database.ts b/src/database/database.ts index 623e8191..906d5070 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,5 +1,5 @@ import get from "lodash.get" -import { paginate } from "../crud/index" +import { paginate } from "../crud/paginate" import { Store } from "../types" import { WertikApp } from "../types" @@ -28,7 +28,7 @@ export const applyRelationshipsFromStoreToGraphql = async ( store.graphql.resolvers[element.currentModule] = { ...oldResolvers, - [element.graphqlKey]: async (parent, _args, context) => { + [element.graphqlKey]: async (parent, _args, context, info) => { const tableInstance = context.wertik.modules[element.referencedModule].tableInstance let referencedModuleKey = @@ -46,6 +46,9 @@ export const applyRelationshipsFromStoreToGraphql = async ( }, }) } else if (["hasMany", "belongsToMany"]) { + if (parent[info.fieldName]) { + return { list: parent[info.fieldName] } + } return await paginate( { where: { diff --git a/src/devServer.ts b/src/devServer.ts index d9c5e09a..f2552ddc 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -30,7 +30,6 @@ wertik({ database: "wapgee_prod_new", graphqlKey: "posts", module: "Post", - allowFiltering: true, options: { as: "posts", foreignKey: "created_by", diff --git a/src/index.ts b/src/index.ts index 72e6b77e..40bab8b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -171,6 +171,10 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( configuration, }) + if (wertikApp?.models) { + store.database.models = wertikApp.models + } + if (configuration.graphql) { wertikApp.graphql = configuration.graphql({ wertikApp: wertikApp, diff --git a/src/modules/modules.ts b/src/modules/modules.ts index a17986c9..c9c9102a 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -25,8 +25,9 @@ export const useModule = (moduleProps: useModuleProps) => { configuration: WertikConfiguration app: WertikApp }) => { + const currentModuleRelationships = [] let tableInstance: ModelCtor> - let graphqlSchema = [] + let graphqlSchema = [`type ${moduleProps.name} {`] let listSchema = "" let filterSchema = [`input ${moduleProps.name}FilterInput {`] @@ -69,6 +70,73 @@ export const useModule = (moduleProps: useModuleProps) => { }, 2500) } + const hasOne = (params: RelationParams) => { + graphqlSchema.push(`${params.graphqlKey}: ${params.module}`) + let relationshipInfo = { + currentModule: moduleProps.name, + currentModuleDatabase: moduleProps.database, + graphqlKey: params.graphqlKey, + referencedModule: params.module, + referencedModuleDatabase: params.database, + options: params.options, + type: "hasOne", + } + store.database.relationships.push(relationshipInfo) + currentModuleRelationships.push(relationshipInfo) + } + const belongsTo = (params: RelationParams) => { + graphqlSchema.push(`${params.graphqlKey}: ${params.module}`) + let relationshipInfo = { + currentModule: moduleProps.name, + currentModuleDatabase: moduleProps.database, + graphqlKey: params.graphqlKey, + referencedModule: params.module, + referencedModuleDatabase: params.database, + options: params.options, + type: "belongsTo", + } + store.database.relationships.push(relationshipInfo) + currentModuleRelationships.push(relationshipInfo) + } + const belongsToMany = (params: RelationParams) => { + graphqlSchema.push(`${params.graphqlKey}: ${params.module}List`) + let relationshipInfo = { + currentModule: moduleProps.name, + currentModuleDatabase: moduleProps.database, + graphqlKey: params.graphqlKey, + referencedModule: params.module, + referencedModuleDatabase: params.database, + options: params.options, + type: "belongsToMany", + } + store.database.relationships.push(relationshipInfo) + currentModuleRelationships.push(relationshipInfo) + } + const hasMany = (params: RelationParams) => { + graphqlSchema.push(`${params.graphqlKey}: ${params.module}List`) + let relationshipInfo = { + currentModule: moduleProps.name, + currentModuleDatabase: moduleProps.database, + graphqlKey: params.graphqlKey, + referencedModule: params.module, + referencedModuleDatabase: params.database, + options: params.options, + type: "hasMany", + } + store.database.relationships.push(relationshipInfo) + currentModuleRelationships.push(relationshipInfo) + } + get(moduleProps, "on", () => {})({ + useQuery, + useMutation, + useExpress, + hasOne, + belongsTo, + belongsToMany, + hasMany, + useSchema, + }) + if (useDatabase) { var createSchema = [] var updateSchema = [] @@ -78,7 +146,6 @@ export const useModule = (moduleProps: useModuleProps) => { moduleProps, connection.instance ) - // console.log(tableInfo) let fields: ModelAttributes, any> = {} @@ -108,9 +175,6 @@ export const useModule = (moduleProps: useModuleProps) => { if (moduleProps?.graphql?.schema) { graphqlSchema = moduleProps.graphql.schema.replace("}", "").split("\n") } else { - // graphql schema - graphqlSchema = [`type ${moduleProps.name} {`] - tableInfo.columns.forEach((columnInfo) => { if (columnInfo.isEnum) { store.graphql.typeDefs = store.graphql.typeDefs.concat( @@ -135,6 +199,11 @@ export const useModule = (moduleProps: useModuleProps) => { filterSchema.push(filterInput) }) + currentModuleRelationships.forEach((relation) => { + filterSchema.push( + `${relation.referencedModule}: ${relation.referencedModule}FilterInput` + ) + }) listSchema = ` query List${moduleProps.name} { @@ -143,69 +212,7 @@ export const useModule = (moduleProps: useModuleProps) => { filters: ${moduleProps.name}Filters } ` - } - const hasOne = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}: ${params.module}`) - store.database.relationships.push({ - currentModule: moduleProps.name, - currentModuleDatabase: moduleProps.database, - graphqlKey: params.graphqlKey, - referencedModule: params.module, - referencedModuleDatabase: params.database, - options: params.options, - type: "hasOne", - }) - } - const belongsTo = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}: ${params.module}`) - store.database.relationships.push({ - currentModule: moduleProps.name, - currentModuleDatabase: moduleProps.database, - graphqlKey: params.graphqlKey, - referencedModule: params.module, - referencedModuleDatabase: params.database, - options: params.options, - type: "belongsTo", - }) - } - const belongsToMany = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}: ${params.module}List`) - store.database.relationships.push({ - currentModule: moduleProps.name, - currentModuleDatabase: moduleProps.database, - graphqlKey: params.graphqlKey, - referencedModule: params.module, - referencedModuleDatabase: params.database, - options: params.options, - type: "belongsToMany", - }) - } - const hasMany = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}: ${params.module}List`) - store.database.relationships.push({ - currentModule: moduleProps.name, - currentModuleDatabase: moduleProps.database, - graphqlKey: params.graphqlKey, - referencedModule: params.module, - referencedModuleDatabase: params.database, - options: params.options, - type: "hasMany", - }) - } - - get(moduleProps, "on", () => {})({ - useQuery, - useMutation, - useExpress, - hasOne, - belongsTo, - belongsToMany, - hasMany, - useSchema, - }) - - if (useDatabase) { graphqlSchema.push("}") filterSchema.push("}") } diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 03495e47..5003fc0d 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -3,6 +3,7 @@ import { useModuleProps } from "../types/modules" import { TableInfo } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" import crud from "../crud" +import store from "../store" export const generateDataTypeFromDescribeTableColumnType = (Type: string) => { let length = Type.match(/[0-9]/g)?.join("") @@ -103,9 +104,11 @@ export const generateEnumTypeForGraphql = (column: TableInfo["columns"][0]) => { }` } - - -export const generateGenerateGraphQLCrud = (props, schemaInformation, store) => { +export const generateGenerateGraphQLCrud = ( + props, + schemaInformation, + store +) => { const { graphql } = crud(props, schemaInformation, store) const resolvers = graphql.generateCrudResolvers() @@ -133,4 +136,19 @@ export const generateGenerateGraphQLCrud = (props, schemaInformation, store) => ...store.graphql.resolvers.Mutation, ...resolvers.Mutation, } -} \ No newline at end of file +} + +/** + * Extract relational fields that were requested in a GraphQL query. + */ +export const getRelationalFieldsRequestedInQuery = ( + module, + requestedFields +) => { + const fields = Object.keys(requestedFields) + // Filter all relationships for provided modules, based on fields provided filter out those relationships. + const relationalFields = store.database.relationships + .filter((c) => c.currentModule === module.name) + .filter((relationship) => fields.includes(relationship.graphqlKey)) + return relationalFields +} diff --git a/src/store.ts b/src/store.ts index ef89cd2b..0e191469 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,6 +1,27 @@ import generalSchema from "./graphql/generalSchema" -const store = { +const store: { + graphql: { + typeDefs: string + resolvers: { + Query: { + [key: string]: Function + } + Mutation: { + [key: string]: Function + } + [key: string]: { + [key: string]: Function + } + } + } + database: { + relationships: any[] + models: { + [key: string]: any + } + } +} = { graphql: { typeDefs: ` ${generalSchema} @@ -30,6 +51,7 @@ const store = { }, database: { relationships: [], + models: {}, }, } diff --git a/src/types/modules.ts b/src/types/modules.ts index 9c9db1e2..f5758ef5 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -37,11 +37,6 @@ export interface RelationParams { options?: { [key: string]: string | number | null }; - /** - * Allow filtering with table as well. - * @default false - */ - allowFiltering?: boolean; } export type useSchemaProps = string @@ -137,7 +132,7 @@ export interface useModuleProps { */ belongsToMany: (props: RelationParams) => {} | void /** - * This method adds belogs to many relationship to a module. + * This method adds belongs to many relationship to a module. */ hasMany: (props: RelationParams) => {} | void /** @@ -146,7 +141,7 @@ export interface useModuleProps { useSchema: (props: useSchemaProps) => {} | void }) => void /** - * Graphql events when a CRUD opreation happens. + * Graphql events when a CRUD operation happens. */ events?: { /** diff --git a/src/utils/convertFiltersIntoSequelizeObject.ts b/src/utils/convertFiltersIntoSequelizeObject.ts index a13c2d3a..f9de4df3 100644 --- a/src/utils/convertFiltersIntoSequelizeObject.ts +++ b/src/utils/convertFiltersIntoSequelizeObject.ts @@ -4,4 +4,4 @@ const convertFiltersIntoSequelizeObject = (filters) => { return filters ? replaceFilterOperators(filters) : {} } -export default async (filters) => convertFiltersIntoSequelizeObject(filters) +export default (filters) => convertFiltersIntoSequelizeObject(filters) From 7383bda06d6356512941ef11355f0c8032a67e51 Mon Sep 17 00:00:00 2001 From: ilyas Date: Fri, 24 Feb 2023 03:13:44 +0500 Subject: [PATCH 04/52] Added eager loading for view query --- src/crud/index.ts | 17 +++++++++++++++++ src/crud/paginate.ts | 1 - src/database/database.ts | 7 ++++--- src/modules/modulesHelpers.ts | 3 ++- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/crud/index.ts b/src/crud/index.ts index 118298c8..b1c93abb 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -164,9 +164,26 @@ export default function (module, schemaInformation, store) { const where = await convertFiltersIntoSequelizeObject( args.where ) + const find = await schemaInformation.tableInstance.findOne({ where: where, + include: getRelationalFieldsRequestedInQuery( + module, + graphqlFields(info) + ).map((c) => { + const _where = convertFiltersIntoSequelizeObject( + get(args, `where.${c.referencedModule}`, {}) + ) + delete where[c.referencedModule] + return { + model: store.database.models[c.referencedModule], + as: c.options.as, + where: _where, + required: false + } + }) }) + return find } ), diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 7ae0bf54..95984887 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -1,7 +1,6 @@ import store from "../store" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import get from "lodash.get" - export const paginate = async (arg, tableInstance, include: any[] = []) => { const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} const offset = limit * (page - 1) diff --git a/src/database/database.ts b/src/database/database.ts index 906d5070..7170bd15 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -39,6 +39,10 @@ export const applyRelationshipsFromStoreToGraphql = async ( referencedModuleKey = "id" } + if (parent[info.fieldName]) { + return { list: parent[info.fieldName] } + } + if (["hasOne", "belongsTo"].includes(element.type)) { return await tableInstance.findOne({ where: { @@ -46,9 +50,6 @@ export const applyRelationshipsFromStoreToGraphql = async ( }, }) } else if (["hasMany", "belongsToMany"]) { - if (parent[info.fieldName]) { - return { list: parent[info.fieldName] } - } return await paginate( { where: { diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 5003fc0d..33e771f9 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -4,6 +4,7 @@ import { TableInfo } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" import crud from "../crud" import store from "../store" +import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" export const generateDataTypeFromDescribeTableColumnType = (Type: string) => { let length = Type.match(/[0-9]/g)?.join("") @@ -151,4 +152,4 @@ export const getRelationalFieldsRequestedInQuery = ( .filter((c) => c.currentModule === module.name) .filter((relationship) => fields.includes(relationship.graphqlKey)) return relationalFields -} +} \ No newline at end of file From 7f7ce0fae0c9acfa643724bb046cf46b226e21dc Mon Sep 17 00:00:00 2001 From: ilyas Date: Fri, 24 Feb 2023 09:24:39 +0500 Subject: [PATCH 05/52] WIP: Eager loading --- src/crud/index.ts | 46 +++++++++----- src/database/database.ts | 17 ++++-- src/database/eagerLoadingGraphqlQuery.ts | 76 ++++++++++++++++++++++++ src/devServer.ts | 12 ++++ src/index.ts | 20 +------ src/modules/modules.ts | 1 + src/store.ts | 20 +++++++ 7 files changed, 154 insertions(+), 38 deletions(-) create mode 100644 src/database/eagerLoadingGraphqlQuery.ts diff --git a/src/crud/index.ts b/src/crud/index.ts index b1c93abb..d5818098 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,4 +1,5 @@ import get from "lodash.get" +import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" import { getRelationalFieldsRequestedInQuery } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" const graphqlFields = require("graphql-fields") @@ -160,6 +161,17 @@ export default function (module, schemaInformation, store) { "events.beforeView", function () {} )(_, args, context, info) + + var relationalFieldsWhere = {} + getRelationalFieldsRequestedInQuery( + module, + graphqlFields(info) + ).forEach((element) => { + relationalFieldsWhere[element.referencedModule] = + args.where[element.referencedModule] + delete args.where[element.referencedModule] + }) + args = argsFromEvent ? argsFromEvent : args const where = await convertFiltersIntoSequelizeObject( args.where @@ -167,21 +179,25 @@ export default function (module, schemaInformation, store) { const find = await schemaInformation.tableInstance.findOne({ where: where, - include: getRelationalFieldsRequestedInQuery( - module, - graphqlFields(info) - ).map((c) => { - const _where = convertFiltersIntoSequelizeObject( - get(args, `where.${c.referencedModule}`, {}) - ) - delete where[c.referencedModule] - return { - model: store.database.models[c.referencedModule], - as: c.options.as, - where: _where, - required: false - } - }) + include: convertGraphqlRequestedFieldsIntoInclude( + graphqlFields(info), + convertFiltersIntoSequelizeObject(relationalFieldsWhere) + ), + // include: getRelationalFieldsRequestedInQuery( + // module, + // graphqlFields(info) + // ).map((c) => { + // const _where = convertFiltersIntoSequelizeObject( + // get(args, `where.${c.referencedModule}`, {}) + // ) + // delete where[c.referencedModule] + // return { + // model: store.database.models[c.referencedModule], + // as: c.options.as, + // where: _where, + // required: false + // } + // }) }) return find diff --git a/src/database/database.ts b/src/database/database.ts index 7170bd15..a60c778e 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -10,6 +10,12 @@ export const applyRelationshipsFromStoreToDatabase = async ( store.database.relationships.forEach((element) => { const currentTable = app.modules[element.currentModule].tableInstance const referencedTable = app.modules[element.referencedModule].tableInstance + + + console.log(` + ${currentTable.getTableName()}.${element.type}(${referencedTable.getTableName()}, ${JSON.stringify(element.options)}) + `) + // element.type will be hasOne, hasMany, belongsTo or belongsToMany currentTable[element.type](referencedTable, element.options || {}) }) @@ -39,17 +45,20 @@ export const applyRelationshipsFromStoreToGraphql = async ( referencedModuleKey = "id" } - if (parent[info.fieldName]) { - return { list: parent[info.fieldName] } - } - if (["hasOne", "belongsTo"].includes(element.type)) { + if (parent[info.fieldName]) { + return parent[info.fieldName] + } return await tableInstance.findOne({ where: { [currentModuleKey]: parent[referencedModuleKey], }, }) } else if (["hasMany", "belongsToMany"]) { + if (parent[info.fieldName]) { + return { list: parent[info.fieldName] } + } + return await paginate( { where: { diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts new file mode 100644 index 00000000..af42af04 --- /dev/null +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -0,0 +1,76 @@ +import store, { wertikApp } from "../store" +import isPlainObject from "lodash.isplainobject" + +const clean = (cleanObject) => { + let recursion = (_obj) => { + Object.keys(_obj).forEach((key) => { + if (key === "list") { + _obj = { ..._obj, ..._obj["list"] } + delete _obj["list"] + } + }) + + Object.keys(_obj).forEach((key) => { + if (isPlainObject(_obj[key])) { + _obj[key] = recursion(_obj[key]) + } + }) + + return _obj + } + + return recursion(cleanObject) +} + +export const convertGraphqlRequestedFieldsIntoInclude = ( + graphqlFields = {}, + where: { [key: string]: any } = {} +) => { + graphqlFields = clean(graphqlFields) + const keys = store.database.relationships.map((c) => c.graphqlKey) + const keysByModules = keys.reduce( + (accumulator, currentValue, currentIndex) => { + return { + ...accumulator, + [currentValue]: + wertikApp.modules[ + store.database.relationships.find( + (c) => c.graphqlKey === currentValue + ).referencedModule + ], + } + }, + {} + ) + + let recursion = (_obj) => { + let includes = [] + + for (const key in _obj) { + if (keys.includes(key)) { + includes.push({ + model: + wertikApp.modules[ + store.database.relationships.find((c) => c.graphqlKey === key) + .referencedModule + ].tableInstance, + as: key, + include: + Object.keys(_obj[key]).length > 0 ? recursion(_obj[key]) : [], + where: {} + }) + } + } + return includes + } + + let include = recursion(graphqlFields); + + include.forEach((item, index) => { + include[index].where = where[keysByModules[item.as].moduleName] ?? {} + }); + + console.log(include[0].include) + + return include; +} diff --git a/src/devServer.ts b/src/devServer.ts index f2552ddc..2eb8fd4e 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -43,6 +43,18 @@ wertik({ useDatabase: true, table: "post", database: "wapgee_prod_new", + on: function ({belongsTo}) { + belongsTo({ + database: "wapgee_prod_new", + graphqlKey: "author", + module: "User", + options: { + as: "author", + sourceKey: "created_by", + foreignKey: "id", + } + }) + } }), test: useModule({ name: "Shirts", diff --git a/src/index.ts b/src/index.ts index 40bab8b8..5d602aa5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import get from "lodash.get" import express from "express" -import store from "./store" +import store, { wertikApp } from "./store" import { applyRelationshipsFromStoreToDatabase, applyRelationshipsFromStoreToGraphql, @@ -32,24 +32,6 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( return new Promise(async (resolve, reject) => { try { configuration.appEnv = configuration.appEnv ?? "local" - const wertikApp: WertikApp = { - appEnv: configuration.appEnv, - port: 1200, - modules: {}, - models: {}, - database: {}, - mailer: {}, - graphql: {}, - sockets: {}, - cronJobs: {}, - storage: {}, - queue: { - jobs: {}, - bullBoard: {}, - }, - redis: {}, - logger: null, - } const port = get(configuration, "port", 1200) const skip = get(configuration, "skip", false) diff --git a/src/modules/modules.ts b/src/modules/modules.ts index c9c9102a..152164db 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -218,6 +218,7 @@ export const useModule = (moduleProps: useModuleProps) => { } const schemaInformation = { + moduleName: moduleProps.name, tableInstance: tableInstance, schema: graphqlSchema.join(`\n`), inputSchema: { diff --git a/src/store.ts b/src/store.ts index 0e191469..8f593080 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,4 +1,24 @@ import generalSchema from "./graphql/generalSchema" +import { WertikApp } from "./types" + +export const wertikApp: WertikApp = { + appEnv: "local", + port: 1200, + modules: {}, + models: {}, + database: {}, + mailer: {}, + graphql: {}, + sockets: {}, + cronJobs: {}, + storage: {}, + queue: { + jobs: {}, + bullBoard: {}, + }, + redis: {}, + logger: null, +} const store: { graphql: { From a2a845d5746851ecae5a6f78a702005ddafb99e0 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 25 Feb 2023 09:28:57 +0500 Subject: [PATCH 06/52] Fixed associations eager loading issue and added logs for when registering logs --- src/crud/index.ts | 17 +---------- src/database/database.ts | 13 +++++---- src/database/eagerLoadingGraphqlQuery.ts | 36 +++++++++--------------- src/database/mysql/mysql.ts | 7 +++-- src/devServer.ts | 20 ++++++------- src/graphql/index.ts | 3 +- src/helpers/modules/backup.ts | 3 +- src/index.ts | 3 +- src/logger/consoleMessages.ts | 6 ++-- src/logger/index.ts | 3 +- src/mailer/index.ts | 13 ++++----- src/modules/modules.ts | 11 ++++---- src/queue/index.ts | 3 +- src/redis/index.ts | 5 ++-- src/sockets/index.ts | 7 +++-- src/storage/index.ts | 7 ++--- src/utils/log.ts | 1 + 17 files changed, 71 insertions(+), 87 deletions(-) create mode 100644 src/utils/log.ts diff --git a/src/crud/index.ts b/src/crud/index.ts index d5818098..8fe0c301 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -4,6 +4,7 @@ import { getRelationalFieldsRequestedInQuery } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" const graphqlFields = require("graphql-fields") import { paginate } from "./paginate" +import {Op} from "sequelize" export default function (module, schemaInformation, store) { return { @@ -176,28 +177,12 @@ export default function (module, schemaInformation, store) { const where = await convertFiltersIntoSequelizeObject( args.where ) - const find = await schemaInformation.tableInstance.findOne({ where: where, include: convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info), convertFiltersIntoSequelizeObject(relationalFieldsWhere) ), - // include: getRelationalFieldsRequestedInQuery( - // module, - // graphqlFields(info) - // ).map((c) => { - // const _where = convertFiltersIntoSequelizeObject( - // get(args, `where.${c.referencedModule}`, {}) - // ) - // delete where[c.referencedModule] - // return { - // model: store.database.models[c.referencedModule], - // as: c.options.as, - // where: _where, - // required: false - // } - // }) }) return find diff --git a/src/database/database.ts b/src/database/database.ts index a60c778e..153f2b5a 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,4 +1,5 @@ import get from "lodash.get" +import { wLog } from "../utils/log" import { paginate } from "../crud/paginate" import { Store } from "../types" import { WertikApp } from "../types" @@ -7,18 +8,21 @@ export const applyRelationshipsFromStoreToDatabase = async ( store: Store, app: WertikApp ) => { + wLog("[Wertik] Registering relationships \n") store.database.relationships.forEach((element) => { const currentTable = app.modules[element.currentModule].tableInstance const referencedTable = app.modules[element.referencedModule].tableInstance - - console.log(` - ${currentTable.getTableName()}.${element.type}(${referencedTable.getTableName()}, ${JSON.stringify(element.options)}) - `) + wLog( + `${currentTable.getTableName()}.${ + element.type + }(${referencedTable.getTableName()}, ${JSON.stringify(element.options)})` + ) // element.type will be hasOne, hasMany, belongsTo or belongsToMany currentTable[element.type](referencedTable, element.options || {}) }) + wLog("\n") } export const applyRelationshipsFromStoreToGraphql = async ( @@ -58,7 +62,6 @@ export const applyRelationshipsFromStoreToGraphql = async ( if (parent[info.fieldName]) { return { list: parent[info.fieldName] } } - return await paginate( { where: { diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index af42af04..e51676ce 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -1,6 +1,11 @@ import store, { wertikApp } from "../store" import isPlainObject from "lodash.isplainobject" +const getModuleNameFromKey = (key: string) => { + return store.database.relationships.find((c) => c.graphqlKey === key) + .referencedModule +} + const clean = (cleanObject) => { let recursion = (_obj) => { Object.keys(_obj).forEach((key) => { @@ -28,20 +33,6 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( ) => { graphqlFields = clean(graphqlFields) const keys = store.database.relationships.map((c) => c.graphqlKey) - const keysByModules = keys.reduce( - (accumulator, currentValue, currentIndex) => { - return { - ...accumulator, - [currentValue]: - wertikApp.modules[ - store.database.relationships.find( - (c) => c.graphqlKey === currentValue - ).referencedModule - ], - } - }, - {} - ) let recursion = (_obj) => { let includes = [] @@ -49,28 +40,27 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( for (const key in _obj) { if (keys.includes(key)) { includes.push({ + required: false, model: - wertikApp.modules[ + wertikApp.models[ store.database.relationships.find((c) => c.graphqlKey === key) .referencedModule - ].tableInstance, + ], as: key, include: Object.keys(_obj[key]).length > 0 ? recursion(_obj[key]) : [], - where: {} }) } } return includes } - let include = recursion(graphqlFields); + let include = recursion(graphqlFields) include.forEach((item, index) => { - include[index].where = where[keysByModules[item.as].moduleName] ?? {} - }); - - console.log(include[0].include) + let __where = where[getModuleNameFromKey(item.as)] + include[index].where = __where + }) - return include; + return include } diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 71bc104f..c79407ec 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -2,6 +2,7 @@ import { Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" import { useMysqlDatabaseProps } from "../../types/database" import get from "lodash.get" +import { wLog } from "../../utils/log" export const getAllRelationships = (dbName: String) => { return ` @@ -25,7 +26,7 @@ export const useMysqlDatabase = function (obj: useMysqlDatabaseProps) { ...(databaseDefaultOptions as any).sql.dbInitializeOptions, }) await sequelize.authenticate() - console.log(`[DB] Succcessfully connected to database ${obj.name}`) + wLog(`[DB] Succcessfully connected to database ${obj.name}`) ;(sequelize as any).relationships = await sequelize.query( getAllRelationships(obj.name) ) @@ -34,8 +35,8 @@ export const useMysqlDatabase = function (obj: useMysqlDatabaseProps) { instance: sequelize, } } catch (e) { - console.log(`[DB] Connecting failed to database ${obj.name}`) - console.log(e.message) + wLog(`[DB] Connecting failed to database ${obj.name}`) + wLog(e.message) } } } diff --git a/src/devServer.ts b/src/devServer.ts index 2eb8fd4e..efc53cc3 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -25,7 +25,7 @@ wertik({ useDatabase: true, table: "users", database: "wapgee_prod_new", - on: function ({hasMany}) { + on: function ({ hasMany }) { hasMany({ database: "wapgee_prod_new", graphqlKey: "posts", @@ -34,27 +34,27 @@ wertik({ as: "posts", foreignKey: "created_by", sourceKey: "id", - } + }, }) - } + }, }), Post: useModule({ name: "Post", useDatabase: true, table: "post", database: "wapgee_prod_new", - on: function ({belongsTo}) { - belongsTo({ - database: "wapgee_prod_new", - graphqlKey: "author", + on: function ({ hasOne }) { + hasOne({ module: "User", + graphqlKey: "author", + database: "default", options: { as: "author", sourceKey: "created_by", foreignKey: "id", - } - }) - } + }, + }); + }, }), test: useModule({ name: "Shirts", diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 4ee88672..39d37e55 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -3,6 +3,7 @@ import omit from "lodash.omit" import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" import { useGraphqlProps, GraphqlInitializeProps } from "../types/graphql" +import {wLog} from "../utils/log" export const useGraphql = (props?: useGraphqlProps) => { return ({ @@ -52,7 +53,7 @@ export const useGraphql = (props?: useGraphqlProps) => { ...(props?.applyMiddlewareOptions ?? {}), }) - console.log( + wLog( `GraphQL server starting at http://localhost:${ configuration.port ?? 1200 }/${props?.applyMiddlewareOptions?.path ?? "graphql"}` diff --git a/src/helpers/modules/backup.ts b/src/helpers/modules/backup.ts index f38867b8..59d71153 100644 --- a/src/helpers/modules/backup.ts +++ b/src/helpers/modules/backup.ts @@ -2,6 +2,7 @@ import moment from "moment" import { useModule } from "../../modules/modules" import mysqldump from "mysqldump" import fs from "fs" +import {wLog} from "../../utils/log" const dumpDatabase = async ( dbName: string, @@ -188,7 +189,7 @@ export const WertikBackupModule = ( return push } catch (e) { - console.log(e) + wLog(e) throw new Error(e) } }, diff --git a/src/index.ts b/src/index.ts index 5d602aa5..56400b00 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import http from "http" import { WertikConfiguration } from "./types" import { WertikApp } from "./types" import { initializeBullBoard } from "./queue/index" +import { wLog } from "./utils/log" export * from "./database/database" export * from "./modules/modules" @@ -175,7 +176,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( setTimeout(async () => { if (skip === false) { httpServer.listen(port, () => { - console.log(`Wertik JS app listening at http://localhost:${port}`) + wLog(`Wertik JS app listening at http://localhost:${port}`) }) } resolve(wertikApp) diff --git a/src/logger/consoleMessages.ts b/src/logger/consoleMessages.ts index 53e31ccc..d93815c5 100644 --- a/src/logger/consoleMessages.ts +++ b/src/logger/consoleMessages.ts @@ -1,9 +1,9 @@ import logSymbols from "log-symbols" import chalk from "chalk" -const log = console.log +import { wLog } from "../utils/log" export const successMessage = function (message, secondMessage?: string) { - log( + wLog( logSymbols.success, ` [Wertik-js]: `, chalk.green(message), @@ -12,7 +12,7 @@ export const successMessage = function (message, secondMessage?: string) { } export const errorMessage = function (message) { - log(logSymbols.error, ` [Wertik-js]:`, chalk.red(message)) + wLog(logSymbols.error, ` [Wertik-js]:`, chalk.red(message)) } export const warningMessage = function () {} diff --git a/src/logger/index.ts b/src/logger/index.ts index 4b7786cc..8c9a46b7 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,4 +1,5 @@ import winston, { LoggerOptions } from "winston" +import {wLog} from "../utils/log" /** * Creates a winston instance @@ -6,7 +7,7 @@ import winston, { LoggerOptions } from "winston" * @returns winston instance */ export const useLogger = (options?: LoggerOptions) => { - console.log(`[Logger]`, `Initialized winston logger`) + wLog(`[Logger]`, `Initialized winston logger`) return winston.createLogger(options) } /** diff --git a/src/mailer/index.ts b/src/mailer/index.ts index 4257aa69..7bd25b83 100644 --- a/src/mailer/index.ts +++ b/src/mailer/index.ts @@ -2,7 +2,7 @@ import nodemailer from "nodemailer" import handlebars from "handlebars" import { useMailerProps, WertikApp, WertikConfiguration } from "../types" import { SendEmailProps } from "../types/mailer" -import get from "lodash.get" +import { wLog } from "../utils/log" export const useMailer = (props: useMailerProps) => { return async () => { @@ -22,7 +22,7 @@ export const useMailer = (props: useMailerProps) => { }, } - console.log(`[Mailer]`, `Initialized mailer "${props.name}"`) + wLog(`[Mailer]`, `Initialized mailer "${props.name}"`) return nodemailer.createTransport(emailConfiguration) } @@ -54,13 +54,10 @@ export const emailSender = ({ subject: props.options.subject, }) if (emailInstance && emailInstance.messageId) { - console.log("Message sent: %s", emailInstance.messageId) + wLog("Message sent: %s", emailInstance.messageId) } if (nodemailer && nodemailer.getTestMessageUrl) { - console.log( - "Preview URL: %s", - nodemailer.getTestMessageUrl(emailInstance) - ) + wLog("Preview URL: %s", nodemailer.getTestMessageUrl(emailInstance)) } if (configuration.mailer.events.onEmailSent) { @@ -79,7 +76,7 @@ export const emailSender = ({ return emailInstance } catch (e) { - console.log(e) + wLog(e) if (configuration.mailer.events.onEmailSentFailed) { configuration.mailer.events.onEmailSentFailed({ mailer: props.mailer, diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 152164db..9b4449fa 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -10,6 +10,7 @@ import { import { getMysqlTableInfo } from "../database/mysql/getTableInfo" import { Store, WertikApp, WertikConfiguration } from "./../types/index" import { ModelCtor, Model, ModelAttributes } from "sequelize/types" +import { wLog } from "../utils/log" /** * Wertik js module @@ -71,7 +72,7 @@ export const useModule = (moduleProps: useModuleProps) => { } const hasOne = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}: ${params.module}`) + graphqlSchema.push(`${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}`) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -85,7 +86,7 @@ export const useModule = (moduleProps: useModuleProps) => { currentModuleRelationships.push(relationshipInfo) } const belongsTo = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}: ${params.module}`) + graphqlSchema.push(`${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}`) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -99,7 +100,7 @@ export const useModule = (moduleProps: useModuleProps) => { currentModuleRelationships.push(relationshipInfo) } const belongsToMany = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}: ${params.module}List`) + graphqlSchema.push(`${params.graphqlKey}(pagination: PaginationInput, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List`) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -113,7 +114,7 @@ export const useModule = (moduleProps: useModuleProps) => { currentModuleRelationships.push(relationshipInfo) } const hasMany = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}: ${params.module}List`) + graphqlSchema.push(`${params.graphqlKey}(pagination: PaginationInput, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List`) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -234,7 +235,7 @@ export const useModule = (moduleProps: useModuleProps) => { app.models[moduleProps.name] = tableInstance } - console.log(`[Module]`, `Initialized module "${moduleProps.name}"`) + wLog(`[Module]`, `Initialized module "${moduleProps.name}"`) return schemaInformation } diff --git a/src/queue/index.ts b/src/queue/index.ts index 27040778..e954ebc2 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -2,6 +2,7 @@ import Queue from "bull" import { isPackageInstalled } from "../utils/checkInstalledPackages" import { WertikApp, WertikConfiguration } from "../types" import { useQueueProps } from "./../types/queue" +import {wLog} from "../utils/log" /** * @param name @@ -51,7 +52,7 @@ export const initializeBullBoard = (props: { serverAdapter.setBasePath(queuePath) express.use(queuePath, serverAdapter.getRouter()) - console.log( + wLog( `Queue UI Monitoring Bull Board running at: http://localhost:${props.configuration.port}${queuePath}` ) diff --git a/src/redis/index.ts b/src/redis/index.ts index 1681a089..4429f6af 100644 --- a/src/redis/index.ts +++ b/src/redis/index.ts @@ -1,4 +1,5 @@ import { createClient } from "redis" +import { wLog } from "../utils/log" import { useRedisProps, WertikApp, WertikConfiguration } from "../types" export const useRedis = (props?: useRedisProps) => { @@ -12,9 +13,9 @@ export const useRedis = (props?: useRedisProps) => { const client = createClient(props) await client.connect() client.on("error", (err) => - console.log(`Redis Client ${props.name} Error `, err) + wLog(`Redis Client ${props.name} Error `, err) ) - console.log(`[Redis]`, `Initialized redis "${props.name}"`) + wLog(`[Redis]`, `Initialized redis "${props.name}"`) return client } } diff --git a/src/sockets/index.ts b/src/sockets/index.ts index 4a6982bf..99d106fd 100644 --- a/src/sockets/index.ts +++ b/src/sockets/index.ts @@ -2,6 +2,7 @@ import { Server as SocketIOServer, ServerOptions as SocketIOServerOptions, } from "socket.io" +import { wLog } from "../utils/log" import { Server as WebSocketServer, ServerOptions as WebSocketServerOptions, @@ -24,7 +25,7 @@ export const useWebSockets = (props: WebSocketServerOptions = {}) => { if (!props.path) { throw new Error("Path must be passed for useWebSockets") } - console.log( + wLog( `Web Sockets server starting at ws://localhost:${configuration.port}${props.path}` ) return new WebSocketServer({ @@ -49,7 +50,7 @@ export const useIndependentWebSocketsServer = ( configuration: WertikConfiguration wertikApp: WertikApp }) => { - console.log( + wLog( `Web Sockets server starting at ws://localhost:${props.port}/${ props.path ?? "" }` @@ -73,7 +74,7 @@ export const useSocketIO = (props: any = {}) => { configuration: WertikConfiguration wertikApp: WertikApp }) => { - console.log( + wLog( `Socket.IO server starting at http://localhost:${configuration.port}${ props.path ?? "/socket.io" }` diff --git a/src/storage/index.ts b/src/storage/index.ts index a72399c2..4a901c05 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -1,5 +1,6 @@ import { WertikApp, WertikConfiguration } from "../types" import { useStorageProps } from "../types/storage" +import { wLog } from "../utils/log" const DIGITAL_OCEAN = "digitalocean" const DROPBOX = "dropbox" @@ -30,9 +31,7 @@ export const useStorage = (storageItem: useStorageProps) => { endpoint: spacesEndpoint, }) - console.log( - `[Storage] Initialized Digital Ocean instance ${storageItem.name}` - ) + wLog(`[Storage] Initialized Digital Ocean instance ${storageItem.name}`) return { spacesEndpoint, @@ -45,7 +44,7 @@ export const useStorage = (storageItem: useStorageProps) => { accessToken: dropboxOptions.accessToken, }) - console.log(`[Storage] Initialized Dropbox instance ${storageItem.name}`) + wLog(`[Storage] Initialized Dropbox instance ${storageItem.name}`) return { dropbox: dropboxInstance, diff --git a/src/utils/log.ts b/src/utils/log.ts new file mode 100644 index 00000000..2789d043 --- /dev/null +++ b/src/utils/log.ts @@ -0,0 +1 @@ +export const wLog = console.log \ No newline at end of file From d46ee9941271dcd153312f81c232193f6dbed7bd Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 26 Feb 2023 04:14:47 +0500 Subject: [PATCH 07/52] Finished eager loading --- package.json | 1 + src/crud/index.ts | 21 ++++---------- src/crud/paginate.ts | 15 +--------- src/database/database.ts | 4 +-- src/database/eagerLoadingGraphqlQuery.ts | 37 +++++++++++++++++------- src/modules/modules.ts | 14 ++------- 6 files changed, 38 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index ea304516..c2378d08 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "graphql-type-json": "^0.3.2", "handlebars": "^4.5.3", "lodash.get": "^4.4.2", + "lodash.has": "^4.5.2", "lodash.isplainobject": "^4.0.6", "lodash.omit": "^4.5.0", "log-symbols": "^3.0.0", diff --git a/src/crud/index.ts b/src/crud/index.ts index 8fe0c301..bd14e69f 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -4,7 +4,7 @@ import { getRelationalFieldsRequestedInQuery } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" const graphqlFields = require("graphql-fields") import { paginate } from "./paginate" -import {Op} from "sequelize" +import { Op } from "sequelize" export default function (module, schemaInformation, store) { return { @@ -163,25 +163,15 @@ export default function (module, schemaInformation, store) { function () {} )(_, args, context, info) - var relationalFieldsWhere = {} - getRelationalFieldsRequestedInQuery( - module, - graphqlFields(info) - ).forEach((element) => { - relationalFieldsWhere[element.referencedModule] = - args.where[element.referencedModule] - delete args.where[element.referencedModule] - }) - args = argsFromEvent ? argsFromEvent : args const where = await convertFiltersIntoSequelizeObject( args.where ) + const find = await schemaInformation.tableInstance.findOne({ where: where, include: convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info), - convertFiltersIntoSequelizeObject(relationalFieldsWhere) + graphqlFields(info, {}, { processArguments: true }), ), }) @@ -201,9 +191,8 @@ export default function (module, schemaInformation, store) { return await paginate( args, schemaInformation.tableInstance, - getRelationalFieldsRequestedInQuery( - module, - graphqlFields(info).list + convertGraphqlRequestedFieldsIntoInclude( + graphqlFields(info, {}, { processArguments: true }) ) ) } diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 95984887..46e463bd 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -1,22 +1,9 @@ import store from "../store" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" -import get from "lodash.get" -export const paginate = async (arg, tableInstance, include: any[] = []) => { +export const paginate = async (arg, tableInstance, includes: any[] = []) => { const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} const offset = limit * (page - 1) const where = convertFiltersIntoSequelizeObject(arg.where) - const includes = include.map((c) => { - const _where = convertFiltersIntoSequelizeObject( - get(arg, `where.${c.referencedModule}`, {}) - ) - delete where[c.referencedModule] - return { - model: store.database.models[c.referencedModule], - as: c.options.as, - where: _where, - required: false - } - }) const { count, rows } = await tableInstance.findAndCountAll({ where, diff --git a/src/database/database.ts b/src/database/database.ts index 153f2b5a..c93d040d 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -52,7 +52,7 @@ export const applyRelationshipsFromStoreToGraphql = async ( if (["hasOne", "belongsTo"].includes(element.type)) { if (parent[info.fieldName]) { return parent[info.fieldName] - } + } return await tableInstance.findOne({ where: { [currentModuleKey]: parent[referencedModuleKey], @@ -61,7 +61,7 @@ export const applyRelationshipsFromStoreToGraphql = async ( } else if (["hasMany", "belongsToMany"]) { if (parent[info.fieldName]) { return { list: parent[info.fieldName] } - } + } return await paginate( { where: { diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index e51676ce..9ef221c5 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -1,5 +1,8 @@ import store, { wertikApp } from "../store" import isPlainObject from "lodash.isplainobject" +import get from "lodash.get" +import has from "lodash.has" +import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" const getModuleNameFromKey = (key: string) => { return store.database.relationships.find((c) => c.graphqlKey === key) @@ -16,7 +19,7 @@ const clean = (cleanObject) => { }) Object.keys(_obj).forEach((key) => { - if (isPlainObject(_obj[key])) { + if (isPlainObject(_obj[key]) && key !== "__arguments") { _obj[key] = recursion(_obj[key]) } }) @@ -28,8 +31,7 @@ const clean = (cleanObject) => { } export const convertGraphqlRequestedFieldsIntoInclude = ( - graphqlFields = {}, - where: { [key: string]: any } = {} + graphqlFields = {} ) => { graphqlFields = clean(graphqlFields) const keys = store.database.relationships.map((c) => c.graphqlKey) @@ -39,7 +41,7 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( for (const key in _obj) { if (keys.includes(key)) { - includes.push({ + const includeParams: { [key: string]: any } = { required: false, model: wertikApp.models[ @@ -49,7 +51,27 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( as: key, include: Object.keys(_obj[key]).length > 0 ? recursion(_obj[key]) : [], - }) + } + + let __arguments = get(_obj, `[${key}].__arguments`, []) + let __whereInArguments = __arguments.find((c) => has(c, "where")) + let __limitInArguments = __arguments.find((c) => has(c, "limit")) + let __offsetInArguments = __arguments.find((c) => has(c, "offset")) + __limitInArguments = get(__limitInArguments, "limit.value", null) + __offsetInArguments = get(__offsetInArguments, "offset.value", null) + + if (__whereInArguments) { + __whereInArguments = get(__whereInArguments, "where.value", {}) + __whereInArguments = + convertFiltersIntoSequelizeObject(__whereInArguments) + + includeParams.where = __whereInArguments + } + + if (__limitInArguments) includeParams.limit = __limitInArguments + if (__offsetInArguments) includeParams.offset = __offsetInArguments + + includes.push(includeParams) } } return includes @@ -57,10 +79,5 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let include = recursion(graphqlFields) - include.forEach((item, index) => { - let __where = where[getModuleNameFromKey(item.as)] - include[index].where = __where - }) - return include } diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 9b4449fa..c9ea4d9a 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -26,7 +26,6 @@ export const useModule = (moduleProps: useModuleProps) => { configuration: WertikConfiguration app: WertikApp }) => { - const currentModuleRelationships = [] let tableInstance: ModelCtor> let graphqlSchema = [`type ${moduleProps.name} {`] let listSchema = "" @@ -83,7 +82,6 @@ export const useModule = (moduleProps: useModuleProps) => { type: "hasOne", } store.database.relationships.push(relationshipInfo) - currentModuleRelationships.push(relationshipInfo) } const belongsTo = (params: RelationParams) => { graphqlSchema.push(`${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}`) @@ -97,10 +95,9 @@ export const useModule = (moduleProps: useModuleProps) => { type: "belongsTo", } store.database.relationships.push(relationshipInfo) - currentModuleRelationships.push(relationshipInfo) } const belongsToMany = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}(pagination: PaginationInput, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List`) + graphqlSchema.push(`${params.graphqlKey}(offset: Int, limit: Int, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List`) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -111,10 +108,9 @@ export const useModule = (moduleProps: useModuleProps) => { type: "belongsToMany", } store.database.relationships.push(relationshipInfo) - currentModuleRelationships.push(relationshipInfo) } const hasMany = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}(pagination: PaginationInput, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List`) + graphqlSchema.push(`${params.graphqlKey}(offset: Int, limit: Int, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List`) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -125,7 +121,6 @@ export const useModule = (moduleProps: useModuleProps) => { type: "hasMany", } store.database.relationships.push(relationshipInfo) - currentModuleRelationships.push(relationshipInfo) } get(moduleProps, "on", () => {})({ useQuery, @@ -200,11 +195,6 @@ export const useModule = (moduleProps: useModuleProps) => { filterSchema.push(filterInput) }) - currentModuleRelationships.forEach((relation) => { - filterSchema.push( - `${relation.referencedModule}: ${relation.referencedModule}FilterInput` - ) - }) listSchema = ` query List${moduleProps.name} { From 069555861a8e5c773f6d30b7400e858b5b0bde4c Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 26 Feb 2023 04:25:13 +0500 Subject: [PATCH 08/52] Prettier --- src/crud/index.ts | 2 +- src/database/database.ts | 4 ++-- src/devServer.ts | 2 +- src/graphql/index.ts | 2 +- src/helpers/modules/backup.ts | 2 +- src/logger/consoleMessages.ts | 2 +- src/logger/index.ts | 2 +- src/modules/modules.ts | 16 ++++++++++++---- src/modules/modulesHelpers.ts | 2 +- src/queue/index.ts | 2 +- src/redis/index.ts | 4 +--- src/types/modules.ts | 2 +- src/utils/log.ts | 2 +- 13 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/crud/index.ts b/src/crud/index.ts index bd14e69f..b38537d2 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -171,7 +171,7 @@ export default function (module, schemaInformation, store) { const find = await schemaInformation.tableInstance.findOne({ where: where, include: convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info, {}, { processArguments: true }), + graphqlFields(info, {}, { processArguments: true }) ), }) diff --git a/src/database/database.ts b/src/database/database.ts index c93d040d..153f2b5a 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -52,7 +52,7 @@ export const applyRelationshipsFromStoreToGraphql = async ( if (["hasOne", "belongsTo"].includes(element.type)) { if (parent[info.fieldName]) { return parent[info.fieldName] - } + } return await tableInstance.findOne({ where: { [currentModuleKey]: parent[referencedModuleKey], @@ -61,7 +61,7 @@ export const applyRelationshipsFromStoreToGraphql = async ( } else if (["hasMany", "belongsToMany"]) { if (parent[info.fieldName]) { return { list: parent[info.fieldName] } - } + } return await paginate( { where: { diff --git a/src/devServer.ts b/src/devServer.ts index efc53cc3..1b018d24 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -53,7 +53,7 @@ wertik({ sourceKey: "created_by", foreignKey: "id", }, - }); + }) }, }), test: useModule({ diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 39d37e55..6e73b4d1 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -3,7 +3,7 @@ import omit from "lodash.omit" import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" import { useGraphqlProps, GraphqlInitializeProps } from "../types/graphql" -import {wLog} from "../utils/log" +import { wLog } from "../utils/log" export const useGraphql = (props?: useGraphqlProps) => { return ({ diff --git a/src/helpers/modules/backup.ts b/src/helpers/modules/backup.ts index 59d71153..d736cbdf 100644 --- a/src/helpers/modules/backup.ts +++ b/src/helpers/modules/backup.ts @@ -2,7 +2,7 @@ import moment from "moment" import { useModule } from "../../modules/modules" import mysqldump from "mysqldump" import fs from "fs" -import {wLog} from "../../utils/log" +import { wLog } from "../../utils/log" const dumpDatabase = async ( dbName: string, diff --git a/src/logger/consoleMessages.ts b/src/logger/consoleMessages.ts index d93815c5..d74cfb03 100644 --- a/src/logger/consoleMessages.ts +++ b/src/logger/consoleMessages.ts @@ -1,6 +1,6 @@ import logSymbols from "log-symbols" import chalk from "chalk" -import { wLog } from "../utils/log" +import { wLog } from "../utils/log" export const successMessage = function (message, secondMessage?: string) { wLog( diff --git a/src/logger/index.ts b/src/logger/index.ts index 8c9a46b7..058ffb4a 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,5 +1,5 @@ import winston, { LoggerOptions } from "winston" -import {wLog} from "../utils/log" +import { wLog } from "../utils/log" /** * Creates a winston instance diff --git a/src/modules/modules.ts b/src/modules/modules.ts index c9ea4d9a..ada22031 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -71,7 +71,9 @@ export const useModule = (moduleProps: useModuleProps) => { } const hasOne = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}`) + graphqlSchema.push( + `${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}` + ) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -84,7 +86,9 @@ export const useModule = (moduleProps: useModuleProps) => { store.database.relationships.push(relationshipInfo) } const belongsTo = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}`) + graphqlSchema.push( + `${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}` + ) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -97,7 +101,9 @@ export const useModule = (moduleProps: useModuleProps) => { store.database.relationships.push(relationshipInfo) } const belongsToMany = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}(offset: Int, limit: Int, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List`) + graphqlSchema.push( + `${params.graphqlKey}(offset: Int, limit: Int, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List` + ) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -110,7 +116,9 @@ export const useModule = (moduleProps: useModuleProps) => { store.database.relationships.push(relationshipInfo) } const hasMany = (params: RelationParams) => { - graphqlSchema.push(`${params.graphqlKey}(offset: Int, limit: Int, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List`) + graphqlSchema.push( + `${params.graphqlKey}(offset: Int, limit: Int, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List` + ) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 33e771f9..6eb8284c 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -152,4 +152,4 @@ export const getRelationalFieldsRequestedInQuery = ( .filter((c) => c.currentModule === module.name) .filter((relationship) => fields.includes(relationship.graphqlKey)) return relationalFields -} \ No newline at end of file +} diff --git a/src/queue/index.ts b/src/queue/index.ts index e954ebc2..4ab692fa 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -2,7 +2,7 @@ import Queue from "bull" import { isPackageInstalled } from "../utils/checkInstalledPackages" import { WertikApp, WertikConfiguration } from "../types" import { useQueueProps } from "./../types/queue" -import {wLog} from "../utils/log" +import { wLog } from "../utils/log" /** * @param name diff --git a/src/redis/index.ts b/src/redis/index.ts index 4429f6af..9c2c2579 100644 --- a/src/redis/index.ts +++ b/src/redis/index.ts @@ -12,9 +12,7 @@ export const useRedis = (props?: useRedisProps) => { }) => { const client = createClient(props) await client.connect() - client.on("error", (err) => - wLog(`Redis Client ${props.name} Error `, err) - ) + client.on("error", (err) => wLog(`Redis Client ${props.name} Error `, err)) wLog(`[Redis]`, `Initialized redis "${props.name}"`) return client } diff --git a/src/types/modules.ts b/src/types/modules.ts index f5758ef5..adbb4f5d 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -36,7 +36,7 @@ export interface RelationParams { database: string options?: { [key: string]: string | number | null - }; + } } export type useSchemaProps = string diff --git a/src/utils/log.ts b/src/utils/log.ts index 2789d043..34450470 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -1 +1 @@ -export const wLog = console.log \ No newline at end of file +export const wLog = console.log From 2d97b5f494e26456cf09e4f307ec237c0951defd Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 26 Feb 2023 08:33:53 +0500 Subject: [PATCH 09/52] Added recursion attributes, filters to eager loading graphql queries --- src/crud/index.ts | 18 +++++++++++++++--- src/crud/paginate.ts | 8 +++++++- src/database/eagerLoadingGraphqlQuery.ts | 4 ++++ src/modules/modulesHelpers.ts | 5 +++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/crud/index.ts b/src/crud/index.ts index b38537d2..89d3688b 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,10 +1,10 @@ import get from "lodash.get" +import { wLog } from "../utils/log" import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" -import { getRelationalFieldsRequestedInQuery } from "../modules/modulesHelpers" +import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" const graphqlFields = require("graphql-fields") import { paginate } from "./paginate" -import { Op } from "sequelize" export default function (module, schemaInformation, store) { return { @@ -157,6 +157,7 @@ export default function (module, schemaInformation, store) { module, "graphql.queries.view", async (_, args, context, info) => { + wLog(`[Wertik-GraphQL-query]: view${module.name}`) const argsFromEvent = await get( module, "events.beforeView", @@ -170,6 +171,9 @@ export default function (module, schemaInformation, store) { const find = await schemaInformation.tableInstance.findOne({ where: where, + attributes: generateRequestedFieldsFromGraphqlInfo( + graphqlFields(info) + ), include: convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }) ), @@ -182,18 +186,25 @@ export default function (module, schemaInformation, store) { module, "graphql.queries.list", async (_, args, context, info) => { + wLog(`[Wertik-GraphQL-query]: list${module.name}`) const argsFromEvent = await get( module, "events.beforeList", function () {} )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args + return await paginate( args, schemaInformation.tableInstance, convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }) - ) + ), + { + attributes: generateRequestedFieldsFromGraphqlInfo( + graphqlFields(info).list + ), + } ) } ), @@ -201,6 +212,7 @@ export default function (module, schemaInformation, store) { module, "graphql.queries.count", async (_, args, context, info) => { + wLog(`[Wertik-GraphQL-query]: count${module.name}`) const argsFromEvent = await get( module, "events.beforeCount", diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 46e463bd..2f5549a9 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -1,6 +1,11 @@ import store from "../store" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" -export const paginate = async (arg, tableInstance, includes: any[] = []) => { +export const paginate = async ( + arg, + tableInstance, + includes: any[] = [], + queryOptions: { [key: string]: any } = {} +) => { const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} const offset = limit * (page - 1) const where = convertFiltersIntoSequelizeObject(arg.where) @@ -11,6 +16,7 @@ export const paginate = async (arg, tableInstance, includes: any[] = []) => { limit, order: sorting.map(({ column, type }) => [column, type]), include: includes, + ...queryOptions, }) const totalPages = Math.ceil(count / limit) diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 9ef221c5..a147d412 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -3,6 +3,7 @@ import isPlainObject from "lodash.isplainobject" import get from "lodash.get" import has from "lodash.has" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" +import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelpers" const getModuleNameFromKey = (key: string) => { return store.database.relationships.find((c) => c.graphqlKey === key) @@ -36,6 +37,8 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( graphqlFields = clean(graphqlFields) const keys = store.database.relationships.map((c) => c.graphqlKey) + + let recursion = (_obj) => { let includes = [] @@ -49,6 +52,7 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( .referencedModule ], as: key, + attributes: generateRequestedFieldsFromGraphqlInfo(_obj[key]), include: Object.keys(_obj[key]).length > 0 ? recursion(_obj[key]) : [], } diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 6eb8284c..f3b07887 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -153,3 +153,8 @@ export const getRelationalFieldsRequestedInQuery = ( .filter((relationship) => fields.includes(relationship.graphqlKey)) return relationalFields } + +export const generateRequestedFieldsFromGraphqlInfo = (info) => { + const keys = [...store.database.relationships.map((c) => c.graphqlKey), '__typename', '__arguments'] + return Object.keys(info).filter((c) => !keys.includes(c)) +} From de9687d58aa5a25f067819fb325122b0194db4a4 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 26 Feb 2023 08:34:16 +0500 Subject: [PATCH 10/52] Run prettier --- src/database/eagerLoadingGraphqlQuery.ts | 2 -- src/modules/modulesHelpers.ts | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index a147d412..298a94b7 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -37,8 +37,6 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( graphqlFields = clean(graphqlFields) const keys = store.database.relationships.map((c) => c.graphqlKey) - - let recursion = (_obj) => { let includes = [] diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index f3b07887..d5751e94 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -155,6 +155,10 @@ export const getRelationalFieldsRequestedInQuery = ( } export const generateRequestedFieldsFromGraphqlInfo = (info) => { - const keys = [...store.database.relationships.map((c) => c.graphqlKey), '__typename', '__arguments'] + const keys = [ + ...store.database.relationships.map((c) => c.graphqlKey), + "__typename", + "__arguments", + ] return Object.keys(info).filter((c) => !keys.includes(c)) } From e0b48ab3878c0fe6cd3075af3580563cf23be8db Mon Sep 17 00:00:00 2001 From: ilyas Date: Mon, 27 Feb 2023 10:33:51 +0500 Subject: [PATCH 11/52] Added prettier to commit --- .huskyrc | 5 +++++ .prettierrc.json => .prettierrc | 0 package.json | 1 + 3 files changed, 6 insertions(+) create mode 100644 .huskyrc rename .prettierrc.json => .prettierrc (100%) diff --git a/.huskyrc b/.huskyrc new file mode 100644 index 00000000..73c98cc9 --- /dev/null +++ b/.huskyrc @@ -0,0 +1,5 @@ +{ + "hooks": { + "pre-commit": "npm run prettier" + } +} \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc similarity index 100% rename from .prettierrc.json rename to .prettierrc diff --git a/package.json b/package.json index c2378d08..b4d21754 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "@types/winston": "^2.4.4", "concurrently": "^3.6.1", "dotenv": "^16.0.0", + "husky": "^8.0.3", "jest": "^27.4.7", "nodemon": "^1.18.9", "prettier": "^2.3.2", From 14cee702fa2b3683b3b87dfef661c5e7a30647b4 Mon Sep 17 00:00:00 2001 From: ilyas Date: Mon, 27 Feb 2023 11:35:51 +0500 Subject: [PATCH 12/52] Change in test and logs in database.ts file --- src/database/database.ts | 5 ++-- tests/index.test.js | 60 +++++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/database/database.ts b/src/database/database.ts index 153f2b5a..f4f55d30 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -8,7 +8,8 @@ export const applyRelationshipsFromStoreToDatabase = async ( store: Store, app: WertikApp ) => { - wLog("[Wertik] Registering relationships \n") + if (store.database.relationships.length > 0) + wLog("[Wertik] Registering relationships \n") store.database.relationships.forEach((element) => { const currentTable = app.modules[element.currentModule].tableInstance const referencedTable = app.modules[element.referencedModule].tableInstance @@ -22,7 +23,7 @@ export const applyRelationshipsFromStoreToDatabase = async ( // element.type will be hasOne, hasMany, belongsTo or belongsToMany currentTable[element.type](referencedTable, element.options || {}) }) - wLog("\n") + if (store.database.relationships.length > 0) wLog("\n") } export const applyRelationshipsFromStoreToGraphql = async ( diff --git a/tests/index.test.js b/tests/index.test.js index 767ad6b0..73e20582 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -33,34 +33,38 @@ test("Expect null configuration does not causes error", async () => { await expect(wertik(null)).resolves.not.toThrowError() }) -test("Expect test database to connect and does not causes error", async () => { - await expect( - wertik({ - database: { - default: useMysqlDatabase(database), - }, - }) - ).resolves.not.toThrowError() -}) +if (database.name) { + test("Expect test database to connect and does not causes error", async () => { + await expect( + wertik({ + database: { + default: useMysqlDatabase(database), + }, + }) + ).resolves.not.toThrowError() + }) +} -test("Expect useMysqlDatabase, useModule and useGraphql", async () => { - await expect( - wertik({ - database: { - default: useMysqlDatabase(database), - }, - modules: { - test: useModule({ - name: "Shirts", - useDatabase: true, - database: "default", - table: process.env.TEST_DATABASE_TABLE, - }), - }, - graphql: useGraphql(), - }) - ).resolves.not.toThrowError() -}) +if (database.name) { + test("Expect useMysqlDatabase, useModule and useGraphql", async () => { + await expect( + wertik({ + database: { + default: useMysqlDatabase(database), + }, + modules: { + test: useModule({ + name: "Shirts", + useDatabase: true, + database: "default", + table: process.env.TEST_DATABASE_TABLE, + }), + }, + graphql: useGraphql(), + }) + ).resolves.not.toThrowError() + }) +} test("Expect mailer to work without configuration and does not causes error", async () => { await expect( @@ -82,8 +86,6 @@ test("Expect graphql to work with useGraphql and does not causes error", async ( ).resolves.not.toThrowError() }) - - test("Expect useWebsockets, useIndependentWebSocketsServer and useSocketIO works and does not throw any error", async () => { await expect( wertik({ From bea149e663f5aa8309e6be064fd6f302c2da26ef Mon Sep 17 00:00:00 2001 From: ilyas Date: Mon, 27 Feb 2023 13:51:02 +0500 Subject: [PATCH 13/52] Changed messages --- package.json | 2 +- src/crud/index.ts | 33 +++++++++++++++++++++++++++++---- src/database/database.ts | 4 ++-- src/database/mysql/mysql.ts | 4 ++-- src/devServer.ts | 30 +++++++++++++++++++++++++++++- src/graphql/index.ts | 7 ++++--- src/helpers/modules/backup.ts | 4 ++-- src/index.ts | 4 ++-- src/logger/index.ts | 4 ++-- src/mailer/index.ts | 4 ++-- src/modules/modules.ts | 4 ++-- src/sockets/index.ts | 19 +++++++++++-------- src/utils/dayjs.ts | 6 ++++++ src/utils/log.ts | 14 ++++++++++++++ 14 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 src/utils/dayjs.ts diff --git a/package.json b/package.json index b4d21754..0624e53e 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "chalk": "^3.0.0", "cors": "^2.8.5", "cross-env": "^7.0.3", + "dayjs": "^1.11.7", "dropbox": "^8.2.0", "express": "^4.17.1", "graphql": "^15.7.2", @@ -54,7 +55,6 @@ "lodash.isplainobject": "^4.0.6", "lodash.omit": "^4.5.0", "log-symbols": "^3.0.0", - "moment": "^2.23.0", "morgan": "^1.9.1", "multer": "^1.4.2", "mysql2": "^1.6.4", diff --git a/src/crud/index.ts b/src/crud/index.ts index 89d3688b..08d3e4cc 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,5 +1,5 @@ import get from "lodash.get" -import { wLog } from "../utils/log" +import { wLog, wLogWithDateWithInfo } from "../utils/log" import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" @@ -49,6 +49,10 @@ export default function (module, schemaInformation, store) { module, "graphql.mutations.createOrUpdate", async (_, args, context, info) => { + wLogWithDateWithInfo( + "[Wertik-GraphQL-Mutation]", + `${info.fieldName} - ${JSON.stringify(args)}` + ) const argsFromEvent = await get( module, "events.beforeCreateOrUpdate", @@ -86,6 +90,10 @@ export default function (module, schemaInformation, store) { module, "graphql.mutations.update", async (_, args, context, info) => { + wLogWithDateWithInfo( + "[Wertik-GraphQL-Mutation]", + `${info.fieldName} - ${JSON.stringify(args)}` + ) const argsFromEvent = await get( module, "events.beforeUpdate", @@ -114,6 +122,10 @@ export default function (module, schemaInformation, store) { module, "graphql.mutations.delete", async (_, args, context, info) => { + wLogWithDateWithInfo( + "[Wertik-GraphQL-Mutation]", + `${info.fieldName} - ${JSON.stringify(args)}` + ) const argsFromEvent = await get( module, "events.beforeDelete", @@ -133,6 +145,10 @@ export default function (module, schemaInformation, store) { module, "graphql.mutations.create", async (_, args, context, info) => { + wLogWithDateWithInfo( + "[Wertik-GraphQL-Mutation]", + `${info.fieldName} - ${JSON.stringify(args)}` + ) const argsFromEvent = await get( module, "events.beforeCreate", @@ -157,7 +173,10 @@ export default function (module, schemaInformation, store) { module, "graphql.queries.view", async (_, args, context, info) => { - wLog(`[Wertik-GraphQL-query]: view${module.name}`) + wLogWithDateWithInfo( + "[Wertik-GraphQL-Query]", + `${info.fieldName} - ${JSON.stringify(args)}` + ) const argsFromEvent = await get( module, "events.beforeView", @@ -186,7 +205,10 @@ export default function (module, schemaInformation, store) { module, "graphql.queries.list", async (_, args, context, info) => { - wLog(`[Wertik-GraphQL-query]: list${module.name}`) + wLogWithDateWithInfo( + "[Wertik-GraphQL-Query]", + `list${module.name} - args ${JSON.stringify(args)}` + ) const argsFromEvent = await get( module, "events.beforeList", @@ -212,7 +234,10 @@ export default function (module, schemaInformation, store) { module, "graphql.queries.count", async (_, args, context, info) => { - wLog(`[Wertik-GraphQL-query]: count${module.name}`) + wLogWithDateWithInfo( + "[Wertik-GraphQL-Query]", + `${info.fieldName} - ${JSON.stringify(args)}` + ) const argsFromEvent = await get( module, "events.beforeCount", diff --git a/src/database/database.ts b/src/database/database.ts index f4f55d30..431ef229 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,5 +1,5 @@ import get from "lodash.get" -import { wLog } from "../utils/log" +import { wLog, wLogWithInfo } from "../utils/log" import { paginate } from "../crud/paginate" import { Store } from "../types" import { WertikApp } from "../types" @@ -9,7 +9,7 @@ export const applyRelationshipsFromStoreToDatabase = async ( app: WertikApp ) => { if (store.database.relationships.length > 0) - wLog("[Wertik] Registering relationships \n") + wLogWithInfo("[Wertik-Database]", "Registering relationships \n") store.database.relationships.forEach((element) => { const currentTable = app.modules[element.currentModule].tableInstance const referencedTable = app.modules[element.referencedModule].tableInstance diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index c79407ec..cc573f78 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -2,7 +2,7 @@ import { Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" import { useMysqlDatabaseProps } from "../../types/database" import get from "lodash.get" -import { wLog } from "../../utils/log" +import { wLog, wLogWithSuccess } from "../../utils/log" export const getAllRelationships = (dbName: String) => { return ` @@ -26,7 +26,7 @@ export const useMysqlDatabase = function (obj: useMysqlDatabaseProps) { ...(databaseDefaultOptions as any).sql.dbInitializeOptions, }) await sequelize.authenticate() - wLog(`[DB] Succcessfully connected to database ${obj.name}`) + wLogWithSuccess(`[Wertik-Mysql-Database]`,` Successfully connected to database ${obj.name}`) ;(sequelize as any).relationships = await sequelize.query( getAllRelationships(obj.name) ) diff --git a/src/devServer.ts b/src/devServer.ts index 1b018d24..6ce85a08 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -1,4 +1,4 @@ -import wertik, { useMysqlDatabase, useGraphql, useModule } from "./index" +import wertik, { useMysqlDatabase, useGraphql, useModule, useWebSockets, useSocketIO, useIndependentWebSocketsServer, useLogger, useWinstonTransport, useMailer } from "./index" wertik({ port: 1200, @@ -63,4 +63,32 @@ wertik({ table: "shirts", }), }, + sockets: { + mySockets: useWebSockets({ + path: "/websockets", + }), + socketio: useSocketIO({ + path: "/mysocketioserver", + }), + mySockets2: useIndependentWebSocketsServer({ + port: 1500, + }), + }, + logger: useLogger({ + transports: useWinstonTransport((winston) => { + return [ + new winston.transports.File({ + filename: "info.log", + level: "info", + }), + ] + }), + }), + mailer: { + instances: { + default: useMailer({ + name: "Default", + }), + } + }, }) diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 6e73b4d1..534c63ca 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -3,7 +3,7 @@ import omit from "lodash.omit" import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" import { useGraphqlProps, GraphqlInitializeProps } from "../types/graphql" -import { wLog } from "../utils/log" +import { wLog, wLogWithInfo, wLogWithSuccess } from "../utils/log" export const useGraphql = (props?: useGraphqlProps) => { return ({ @@ -53,8 +53,9 @@ export const useGraphql = (props?: useGraphqlProps) => { ...(props?.applyMiddlewareOptions ?? {}), }) - wLog( - `GraphQL server starting at http://localhost:${ + wLogWithSuccess( + '[Wertik-Graphql]', + `http://localhost:${ configuration.port ?? 1200 }/${props?.applyMiddlewareOptions?.path ?? "graphql"}` ) diff --git a/src/helpers/modules/backup.ts b/src/helpers/modules/backup.ts index d736cbdf..336ac183 100644 --- a/src/helpers/modules/backup.ts +++ b/src/helpers/modules/backup.ts @@ -1,4 +1,4 @@ -import moment from "moment" +import dayjs from "./../../utils/dayjs" import { useModule } from "../../modules/modules" import mysqldump from "mysqldump" import fs from "fs" @@ -14,7 +14,7 @@ const dumpDatabase = async ( name: string } ) => { - const filename = `backups/${moment().format( + const filename = `backups/${dayjs().format( "MMMM-DD-YYYY-h-mm-ss-a" )}-database-${dbName}.sql`.toLowerCase() diff --git a/src/index.ts b/src/index.ts index 56400b00..d89c427b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ import http from "http" import { WertikConfiguration } from "./types" import { WertikApp } from "./types" import { initializeBullBoard } from "./queue/index" -import { wLog } from "./utils/log" +import { wLog, wLogWithSuccess } from "./utils/log" export * from "./database/database" export * from "./modules/modules" @@ -176,7 +176,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( setTimeout(async () => { if (skip === false) { httpServer.listen(port, () => { - wLog(`Wertik JS app listening at http://localhost:${port}`) + wLogWithSuccess(`[Wertik-App]`,`http://localhost:${port}`) }) } resolve(wertikApp) diff --git a/src/logger/index.ts b/src/logger/index.ts index 058ffb4a..362b88b2 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,5 +1,5 @@ import winston, { LoggerOptions } from "winston" -import { wLog } from "../utils/log" +import { wLog, wLogWithSuccess } from "../utils/log" /** * Creates a winston instance @@ -7,7 +7,7 @@ import { wLog } from "../utils/log" * @returns winston instance */ export const useLogger = (options?: LoggerOptions) => { - wLog(`[Logger]`, `Initialized winston logger`) + wLogWithSuccess(`[Wertik-WinstonLogger]`, `Initialized winston logger`) return winston.createLogger(options) } /** diff --git a/src/mailer/index.ts b/src/mailer/index.ts index 7bd25b83..4de6e7b1 100644 --- a/src/mailer/index.ts +++ b/src/mailer/index.ts @@ -2,7 +2,7 @@ import nodemailer from "nodemailer" import handlebars from "handlebars" import { useMailerProps, WertikApp, WertikConfiguration } from "../types" import { SendEmailProps } from "../types/mailer" -import { wLog } from "../utils/log" +import { wLog, wLogWithInfo, wLogWithSuccess } from "../utils/log" export const useMailer = (props: useMailerProps) => { return async () => { @@ -22,7 +22,7 @@ export const useMailer = (props: useMailerProps) => { }, } - wLog(`[Mailer]`, `Initialized mailer "${props.name}"`) + wLogWithSuccess(`[Wertik-NodeMailer]`, `Initialized mailer "${props.name}"`) return nodemailer.createTransport(emailConfiguration) } diff --git a/src/modules/modules.ts b/src/modules/modules.ts index ada22031..5e54b759 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -10,7 +10,7 @@ import { import { getMysqlTableInfo } from "../database/mysql/getTableInfo" import { Store, WertikApp, WertikConfiguration } from "./../types/index" import { ModelCtor, Model, ModelAttributes } from "sequelize/types" -import { wLog } from "../utils/log" +import { wLog, wLogWithInfo } from "../utils/log" /** * Wertik js module @@ -233,7 +233,7 @@ export const useModule = (moduleProps: useModuleProps) => { app.models[moduleProps.name] = tableInstance } - wLog(`[Module]`, `Initialized module "${moduleProps.name}"`) + wLogWithInfo(`[Wertik-Module]`, `Initialized module "${moduleProps.name}"`) return schemaInformation } diff --git a/src/sockets/index.ts b/src/sockets/index.ts index 99d106fd..3ee953f2 100644 --- a/src/sockets/index.ts +++ b/src/sockets/index.ts @@ -2,7 +2,7 @@ import { Server as SocketIOServer, ServerOptions as SocketIOServerOptions, } from "socket.io" -import { wLog } from "../utils/log" +import { wLog, wLogWithSuccess } from "../utils/log" import { Server as WebSocketServer, ServerOptions as WebSocketServerOptions, @@ -25,8 +25,9 @@ export const useWebSockets = (props: WebSocketServerOptions = {}) => { if (!props.path) { throw new Error("Path must be passed for useWebSockets") } - wLog( - `Web Sockets server starting at ws://localhost:${configuration.port}${props.path}` + wLogWithSuccess( + `[Wertik-WebSockets]`, + `ws://localhost:${configuration.port}${props.path}` ) return new WebSocketServer({ server: wertikApp.httpServer, @@ -50,8 +51,9 @@ export const useIndependentWebSocketsServer = ( configuration: WertikConfiguration wertikApp: WertikApp }) => { - wLog( - `Web Sockets server starting at ws://localhost:${props.port}/${ + wLogWithSuccess( + "[Wertik-WebSocket]", + `ws://localhost:${props.port}/${ props.path ?? "" }` ) @@ -74,9 +76,10 @@ export const useSocketIO = (props: any = {}) => { configuration: WertikConfiguration wertikApp: WertikApp }) => { - wLog( - `Socket.IO server starting at http://localhost:${configuration.port}${ - props.path ?? "/socket.io" + wLogWithSuccess( + `[Wertik-Socket.IO]`, + `http://localhost:${configuration.port}${ + props.path ?? "/socket.io" }` ) return new SocketIOServer(wertikApp.httpServer, props ?? {}) diff --git a/src/utils/dayjs.ts b/src/utils/dayjs.ts new file mode 100644 index 00000000..80d37f92 --- /dev/null +++ b/src/utils/dayjs.ts @@ -0,0 +1,6 @@ +import dayjs from "dayjs" +import localizedFormat from "dayjs/plugin/localizedFormat" + +dayjs.extend(localizedFormat) + +export default dayjs diff --git a/src/utils/log.ts b/src/utils/log.ts index 34450470..3cf8f4c0 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -1 +1,15 @@ +import dayjs from "../utils/dayjs" +import chalk from "chalk" + export const wLog = console.log +export const wLogWithDateWithInfo = (info, ...params) => { + console.log(dayjs().format("L-LT"), chalk.blueBright(info), ...params) +} + +export const wLogWithInfo = (info, ...params) => { + console.log(chalk.blueBright(info), ...params) +} + +export const wLogWithSuccess = (info, ...params) => { + console.log(chalk.greenBright(info), ...params) +} From bcd0781fe200653be6e614f0e55c41ff6ed082ca Mon Sep 17 00:00:00 2001 From: ilyas Date: Mon, 27 Feb 2023 13:56:03 +0500 Subject: [PATCH 14/52] Fixed imports in crud/index.ts --- src/crud/index.ts | 4 ++-- src/database/mysql/mysql.ts | 5 ++++- src/devServer.ts | 14 ++++++++++++-- src/graphql/index.ts | 8 ++++---- src/index.ts | 2 +- src/sockets/index.ts | 8 ++------ 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/crud/index.ts b/src/crud/index.ts index 08d3e4cc..283febed 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,9 +1,9 @@ import get from "lodash.get" -import { wLog, wLogWithDateWithInfo } from "../utils/log" +import { wLogWithDateWithInfo } from "../utils/log" import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" -const graphqlFields = require("graphql-fields") +import graphqlFields from "graphql-fields" import { paginate } from "./paginate" export default function (module, schemaInformation, store) { diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index cc573f78..3ae83ae7 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -26,7 +26,10 @@ export const useMysqlDatabase = function (obj: useMysqlDatabaseProps) { ...(databaseDefaultOptions as any).sql.dbInitializeOptions, }) await sequelize.authenticate() - wLogWithSuccess(`[Wertik-Mysql-Database]`,` Successfully connected to database ${obj.name}`) + wLogWithSuccess( + `[Wertik-Mysql-Database]`, + ` Successfully connected to database ${obj.name}` + ) ;(sequelize as any).relationships = await sequelize.query( getAllRelationships(obj.name) ) diff --git a/src/devServer.ts b/src/devServer.ts index 6ce85a08..dcbf4e62 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -1,4 +1,14 @@ -import wertik, { useMysqlDatabase, useGraphql, useModule, useWebSockets, useSocketIO, useIndependentWebSocketsServer, useLogger, useWinstonTransport, useMailer } from "./index" +import wertik, { + useMysqlDatabase, + useGraphql, + useModule, + useWebSockets, + useSocketIO, + useIndependentWebSocketsServer, + useLogger, + useWinstonTransport, + useMailer, +} from "./index" wertik({ port: 1200, @@ -89,6 +99,6 @@ wertik({ default: useMailer({ name: "Default", }), - } + }, }, }) diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 534c63ca..d0137b1b 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -54,10 +54,10 @@ export const useGraphql = (props?: useGraphqlProps) => { }) wLogWithSuccess( - '[Wertik-Graphql]', - `http://localhost:${ - configuration.port ?? 1200 - }/${props?.applyMiddlewareOptions?.path ?? "graphql"}` + "[Wertik-Graphql]", + `http://localhost:${configuration.port ?? 1200}/${ + props?.applyMiddlewareOptions?.path ?? "graphql" + }` ) return GraphqlApolloServer diff --git a/src/index.ts b/src/index.ts index d89c427b..1e207db6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -176,7 +176,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( setTimeout(async () => { if (skip === false) { httpServer.listen(port, () => { - wLogWithSuccess(`[Wertik-App]`,`http://localhost:${port}`) + wLogWithSuccess(`[Wertik-App]`, `http://localhost:${port}`) }) } resolve(wertikApp) diff --git a/src/sockets/index.ts b/src/sockets/index.ts index 3ee953f2..ab509281 100644 --- a/src/sockets/index.ts +++ b/src/sockets/index.ts @@ -53,9 +53,7 @@ export const useIndependentWebSocketsServer = ( }) => { wLogWithSuccess( "[Wertik-WebSocket]", - `ws://localhost:${props.port}/${ - props.path ?? "" - }` + `ws://localhost:${props.port}/${props.path ?? ""}` ) return new WebSocketServer({ ...props, @@ -78,9 +76,7 @@ export const useSocketIO = (props: any = {}) => { }) => { wLogWithSuccess( `[Wertik-Socket.IO]`, - `http://localhost:${configuration.port}${ - props.path ?? "/socket.io" - }` + `http://localhost:${configuration.port}${props.path ?? "/socket.io"}` ) return new SocketIOServer(wertikApp.httpServer, props ?? {}) } From 3ac3bdeb811139dd88b31d8a8a264dc33b83c067 Mon Sep 17 00:00:00 2001 From: ilyas Date: Mon, 27 Feb 2023 16:27:36 +0500 Subject: [PATCH 15/52] Changing label messages in terminal when starting servers --- src/devServer.ts | 6 ++++++ src/redis/index.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/devServer.ts b/src/devServer.ts index dcbf4e62..738b4f28 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -8,6 +8,7 @@ import wertik, { useLogger, useWinstonTransport, useMailer, + useRedis, } from "./index" wertik({ @@ -101,4 +102,9 @@ wertik({ }), }, }, + redis: { + testRedis: useRedis({ + name: "testRedis" + }) +} }) diff --git a/src/redis/index.ts b/src/redis/index.ts index 9c2c2579..00f32208 100644 --- a/src/redis/index.ts +++ b/src/redis/index.ts @@ -1,5 +1,5 @@ import { createClient } from "redis" -import { wLog } from "../utils/log" +import { wLog, wLogWithSuccess } from "../utils/log" import { useRedisProps, WertikApp, WertikConfiguration } from "../types" export const useRedis = (props?: useRedisProps) => { @@ -13,7 +13,7 @@ export const useRedis = (props?: useRedisProps) => { const client = createClient(props) await client.connect() client.on("error", (err) => wLog(`Redis Client ${props.name} Error `, err)) - wLog(`[Redis]`, `Initialized redis "${props.name}"`) + wLogWithSuccess(`[Wertik-Redis]`, `Initialized redis "${props.name}"`) return client } } From b916240ca4bffee7989ce710fc09e14f59508357 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 4 Mar 2023 02:55:01 +0500 Subject: [PATCH 16/52] crud graphql --- src/crud/index.ts | 31 ++++++++++++++++++++++--------- src/crud/paginate.ts | 5 ++++- src/database/database.ts | 2 ++ src/database/mysql/mysql.ts | 2 +- src/modules/modules.ts | 16 +++++++++++++++- src/store.ts | 2 ++ src/types/index.ts | 3 ++- src/utils/camelize.ts | 7 +++++++ src/utils/voidFunction.ts | 1 + 9 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 src/utils/camelize.ts create mode 100644 src/utils/voidFunction.ts diff --git a/src/crud/index.ts b/src/crud/index.ts index 283febed..13a2a576 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -5,6 +5,8 @@ import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelper import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import graphqlFields from "graphql-fields" import { paginate } from "./paginate" +import omit from "lodash.omit" +import { voidFunction } from "../utils/voidFunction" export default function (module, schemaInformation, store) { return { @@ -56,7 +58,7 @@ export default function (module, schemaInformation, store) { const argsFromEvent = await get( module, "events.beforeCreateOrUpdate", - function () {} + voidFunction )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args const id = args.id @@ -97,7 +99,7 @@ export default function (module, schemaInformation, store) { const argsFromEvent = await get( module, "events.beforeUpdate", - function () {} + voidFunction )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args const where = await convertFiltersIntoSequelizeObject( @@ -129,7 +131,7 @@ export default function (module, schemaInformation, store) { const argsFromEvent = await get( module, "events.beforeDelete", - function () {} + voidFunction )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args const where = await convertFiltersIntoSequelizeObject( @@ -152,7 +154,7 @@ export default function (module, schemaInformation, store) { const argsFromEvent = await get( module, "events.beforeCreate", - function () {} + voidFunction )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args const response = [] @@ -180,8 +182,12 @@ export default function (module, schemaInformation, store) { const argsFromEvent = await get( module, "events.beforeView", - function () {} + voidFunction )(_, args, context, info) + const keys = [ + ...store.database.relationships.map((c) => c.graphqlKey), + ...store.graphql.graphqlKeys, + ] args = argsFromEvent ? argsFromEvent : args const where = await convertFiltersIntoSequelizeObject( @@ -189,7 +195,7 @@ export default function (module, schemaInformation, store) { ) const find = await schemaInformation.tableInstance.findOne({ - where: where, + where: omit(where, keys), attributes: generateRequestedFieldsFromGraphqlInfo( graphqlFields(info) ), @@ -212,7 +218,7 @@ export default function (module, schemaInformation, store) { const argsFromEvent = await get( module, "events.beforeList", - function () {} + voidFunction )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args @@ -241,14 +247,21 @@ export default function (module, schemaInformation, store) { const argsFromEvent = await get( module, "events.beforeCount", - function () {} + voidFunction )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args const where = await convertFiltersIntoSequelizeObject( args.where ) + const keys = [ + ...store.database.relationships.map((c) => c.graphqlKey), + ...store.graphql.graphqlKeys, + ] const count = await schemaInformation.tableInstance.count({ - where: where, + where: omit(where, keys), + include: convertGraphqlRequestedFieldsIntoInclude( + graphqlFields(info, {}, { processArguments: true }) + ), }) return count } diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 2f5549a9..6c1ec23c 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -1,5 +1,7 @@ import store from "../store" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" +import omit from "lodash.omit"; + export const paginate = async ( arg, tableInstance, @@ -8,7 +10,8 @@ export const paginate = async ( ) => { const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} const offset = limit * (page - 1) - const where = convertFiltersIntoSequelizeObject(arg.where) + const keys = [...store.database.relationships.map((c) => c.graphqlKey), ...store.graphql.graphqlKeys] + let where = omit(convertFiltersIntoSequelizeObject(arg.where), keys) const { count, rows } = await tableInstance.findAndCountAll({ where, diff --git a/src/database/database.ts b/src/database/database.ts index 431ef229..eadf87ac 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -3,6 +3,7 @@ import { wLog, wLogWithInfo } from "../utils/log" import { paginate } from "../crud/paginate" import { Store } from "../types" import { WertikApp } from "../types" +import logSymbols from "log-symbols" export const applyRelationshipsFromStoreToDatabase = async ( store: Store, @@ -15,6 +16,7 @@ export const applyRelationshipsFromStoreToDatabase = async ( const referencedTable = app.modules[element.referencedModule].tableInstance wLog( + logSymbols.success, `${currentTable.getTableName()}.${ element.type }(${referencedTable.getTableName()}, ${JSON.stringify(element.options)})` diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 3ae83ae7..ea935223 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -28,7 +28,7 @@ export const useMysqlDatabase = function (obj: useMysqlDatabaseProps) { await sequelize.authenticate() wLogWithSuccess( `[Wertik-Mysql-Database]`, - ` Successfully connected to database ${obj.name}` + `Successfully connected to database ${obj.name}` ) ;(sequelize as any).relationships = await sequelize.query( getAllRelationships(obj.name) diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 5e54b759..9b6078c9 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -10,7 +10,8 @@ import { import { getMysqlTableInfo } from "../database/mysql/getTableInfo" import { Store, WertikApp, WertikConfiguration } from "./../types/index" import { ModelCtor, Model, ModelAttributes } from "sequelize/types" -import { wLog, wLogWithInfo } from "../utils/log" +import { wLogWithInfo } from "../utils/log" +import camelize from "../utils/camelize" /** * Wertik js module @@ -26,6 +27,7 @@ export const useModule = (moduleProps: useModuleProps) => { configuration: WertikConfiguration app: WertikApp }) => { + let currentModuleRelationships = []; let tableInstance: ModelCtor> let graphqlSchema = [`type ${moduleProps.name} {`] let listSchema = "" @@ -84,6 +86,9 @@ export const useModule = (moduleProps: useModuleProps) => { type: "hasOne", } store.database.relationships.push(relationshipInfo) + currentModuleRelationships.push(relationshipInfo); + store.graphql.graphqlKeys.push(camelize(params.module)) + filterSchema.push(`${camelize(params.module)}: ${params.module}FilterInput`); } const belongsTo = (params: RelationParams) => { graphqlSchema.push( @@ -99,6 +104,9 @@ export const useModule = (moduleProps: useModuleProps) => { type: "belongsTo", } store.database.relationships.push(relationshipInfo) + currentModuleRelationships.push(relationshipInfo); + store.graphql.graphqlKeys.push(camelize(params.module)) + filterSchema.push(`${camelize(params.module)}: ${params.module}FilterInput`); } const belongsToMany = (params: RelationParams) => { graphqlSchema.push( @@ -114,6 +122,9 @@ export const useModule = (moduleProps: useModuleProps) => { type: "belongsToMany", } store.database.relationships.push(relationshipInfo) + currentModuleRelationships.push(relationshipInfo); + store.graphql.graphqlKeys.push(camelize(params.module)) + filterSchema.push(`${camelize(params.module)}: ${params.module}FilterInput`); } const hasMany = (params: RelationParams) => { graphqlSchema.push( @@ -128,7 +139,10 @@ export const useModule = (moduleProps: useModuleProps) => { options: params.options, type: "hasMany", } + currentModuleRelationships.push(relationshipInfo); store.database.relationships.push(relationshipInfo) + store.graphql.graphqlKeys.push(camelize(params.module)) + filterSchema.push(`${camelize(params.module)}: ${params.module}FilterInput`); } get(moduleProps, "on", () => {})({ useQuery, diff --git a/src/store.ts b/src/store.ts index 8f593080..d2f65eef 100644 --- a/src/store.ts +++ b/src/store.ts @@ -22,6 +22,7 @@ export const wertikApp: WertikApp = { const store: { graphql: { + graphqlKeys: string[], typeDefs: string resolvers: { Query: { @@ -43,6 +44,7 @@ const store: { } } = { graphql: { + graphqlKeys: [], typeDefs: ` ${generalSchema} type Response { diff --git a/src/types/index.ts b/src/types/index.ts index e51bc069..22803ff6 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -7,7 +7,8 @@ export type iObject = { [key: string]: any } export interface Store { graphql: { - typeDefs: String + graphqlKeys: string[] + typeDefs: string resolvers: { Query: { [key: string]: Function diff --git a/src/utils/camelize.ts b/src/utils/camelize.ts new file mode 100644 index 00000000..b3b64a47 --- /dev/null +++ b/src/utils/camelize.ts @@ -0,0 +1,7 @@ +function camelize(str) { + return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word, index) { + return index === 0 ? word.toLowerCase() : word.toUpperCase(); + }).replace(/\s+/g, ''); +} + +export default camelize \ No newline at end of file diff --git a/src/utils/voidFunction.ts b/src/utils/voidFunction.ts new file mode 100644 index 00000000..8349ba35 --- /dev/null +++ b/src/utils/voidFunction.ts @@ -0,0 +1 @@ +export const voidFunction = function () {} \ No newline at end of file From 9006e80fec34447b0c29b80f17dac8e1cea5e926 Mon Sep 17 00:00:00 2001 From: ilyas Date: Tue, 18 Apr 2023 07:45:47 +0500 Subject: [PATCH 17/52] eager loading mysql query --- src/crud/index.ts | 9 ++++--- src/database/eagerLoadingGraphqlQuery.ts | 32 ++++++++++++++++++++++-- src/devServer.ts | 6 ++--- src/modules/modules.ts | 8 +++--- src/modules/modulesHelpers.ts | 1 + src/utils/defaultOptions.ts | 2 +- src/utils/replaceFilterOperators.ts | 8 +++--- 7 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/crud/index.ts b/src/crud/index.ts index 13a2a576..30d67ed8 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -200,7 +200,8 @@ export default function (module, schemaInformation, store) { graphqlFields(info) ), include: convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info, {}, { processArguments: true }) + graphqlFields(info, {}, { processArguments: true }), + args ), }) @@ -226,7 +227,8 @@ export default function (module, schemaInformation, store) { args, schemaInformation.tableInstance, convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info, {}, { processArguments: true }) + graphqlFields(info, {}, { processArguments: true }), + args ), { attributes: generateRequestedFieldsFromGraphqlInfo( @@ -260,7 +262,8 @@ export default function (module, schemaInformation, store) { const count = await schemaInformation.tableInstance.count({ where: omit(where, keys), include: convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info, {}, { processArguments: true }) + graphqlFields(info, {}, { processArguments: true }), + args ), }) return count diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 298a94b7..a45de369 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -32,10 +32,17 @@ const clean = (cleanObject) => { } export const convertGraphqlRequestedFieldsIntoInclude = ( - graphqlFields = {} + graphqlFields = {}, + args: any = {} ) => { graphqlFields = clean(graphqlFields) - const keys = store.database.relationships.map((c) => c.graphqlKey) + const keys = [ + ...store.database.relationships.map((c) => c.graphqlKey), + ...store.graphql.graphqlKeys, + ] + const requiredFilters = keys.filter((c) => + Object.keys(args.where ?? {}).includes(c) + ) let recursion = (_obj) => { let includes = [] @@ -81,5 +88,26 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let include = recursion(graphqlFields) + + /** + * Make sure the include is required if filters are requested in root level filters. + * If root level filters are not met then the response will be null. + * In below graphql query, it will return if user has id 2 and written a post which id is 132, if id is not found then whole response will be null. + query viewUser { + viewUser(where: { id: { _eq: 2 }, posts: { id: { _eq: 123 } } }) { + id + name + } + } + */ + include = include.map((c) => { + if (requiredFilters.includes(c.as)) { + c.required = true + c.where = convertFiltersIntoSequelizeObject(args.where[c.as]) + } + + return c; + }) + return include } diff --git a/src/devServer.ts b/src/devServer.ts index 738b4f28..8bcc5369 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -104,7 +104,7 @@ wertik({ }, redis: { testRedis: useRedis({ - name: "testRedis" - }) -} + name: "testRedis", + }), + }, }) diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 9b6078c9..73520d75 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -88,7 +88,7 @@ export const useModule = (moduleProps: useModuleProps) => { store.database.relationships.push(relationshipInfo) currentModuleRelationships.push(relationshipInfo); store.graphql.graphqlKeys.push(camelize(params.module)) - filterSchema.push(`${camelize(params.module)}: ${params.module}FilterInput`); + filterSchema.push(`${camelize(params.graphqlKey)}: ${params.module}FilterInput`); } const belongsTo = (params: RelationParams) => { graphqlSchema.push( @@ -106,7 +106,7 @@ export const useModule = (moduleProps: useModuleProps) => { store.database.relationships.push(relationshipInfo) currentModuleRelationships.push(relationshipInfo); store.graphql.graphqlKeys.push(camelize(params.module)) - filterSchema.push(`${camelize(params.module)}: ${params.module}FilterInput`); + filterSchema.push(`${camelize(params.graphqlKey)}: ${params.module}FilterInput`); } const belongsToMany = (params: RelationParams) => { graphqlSchema.push( @@ -124,7 +124,7 @@ export const useModule = (moduleProps: useModuleProps) => { store.database.relationships.push(relationshipInfo) currentModuleRelationships.push(relationshipInfo); store.graphql.graphqlKeys.push(camelize(params.module)) - filterSchema.push(`${camelize(params.module)}: ${params.module}FilterInput`); + filterSchema.push(`${camelize(params.graphqlKey)}: ${params.module}FilterInput`); } const hasMany = (params: RelationParams) => { graphqlSchema.push( @@ -142,7 +142,7 @@ export const useModule = (moduleProps: useModuleProps) => { currentModuleRelationships.push(relationshipInfo); store.database.relationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) - filterSchema.push(`${camelize(params.module)}: ${params.module}FilterInput`); + filterSchema.push(`${camelize(params.graphqlKey)}: ${params.module}FilterInput`); } get(moduleProps, "on", () => {})({ useQuery, diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index d5751e94..0747debb 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -157,6 +157,7 @@ export const getRelationalFieldsRequestedInQuery = ( export const generateRequestedFieldsFromGraphqlInfo = (info) => { const keys = [ ...store.database.relationships.map((c) => c.graphqlKey), + ...store.graphql.graphqlKeys, "__typename", "__arguments", ] diff --git a/src/utils/defaultOptions.ts b/src/utils/defaultOptions.ts index 607aa323..7f5d8be2 100644 --- a/src/utils/defaultOptions.ts +++ b/src/utils/defaultOptions.ts @@ -12,7 +12,7 @@ export const databaseDefaultOptions = { logging: false, operatorsAliases: "0", underscored: false, - freetableName: true, + freezeTableName: true, }, defaultTableOptions: { timestamps: false, diff --git a/src/utils/replaceFilterOperators.ts b/src/utils/replaceFilterOperators.ts index 7f092292..1b55c9e1 100644 --- a/src/utils/replaceFilterOperators.ts +++ b/src/utils/replaceFilterOperators.ts @@ -6,7 +6,7 @@ const wrap = (operator: string) => { return Op[operator.replace("_", "")] } -const iterate = (obj) => { +const replaceFilterOperators = (obj) => { if (isPlainObject(obj)) { Object.keys(obj).forEach((element) => { const value = obj[element] @@ -16,13 +16,13 @@ const iterate = (obj) => { delete obj[element] } if (isPlainObject(value) || Array.isArray(value)) { - iterate(value) + replaceFilterOperators(value) } }) return obj } else if (Array.isArray(obj)) { - obj.forEach((element) => iterate(element)) + obj.forEach((element) => replaceFilterOperators(element)) } } -export default iterate +export default replaceFilterOperators From 2278cffb98f014a21db71f28fe97391921206c17 Mon Sep 17 00:00:00 2001 From: ilyas Date: Tue, 18 Apr 2023 08:15:26 +0500 Subject: [PATCH 18/52] remove husky --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 0624e53e..4955cf9d 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "@types/winston": "^2.4.4", "concurrently": "^3.6.1", "dotenv": "^16.0.0", - "husky": "^8.0.3", "jest": "^27.4.7", "nodemon": "^1.18.9", "prettier": "^2.3.2", From 77d38030c97d2db362e89e8d01a81858fc85b3c2 Mon Sep 17 00:00:00 2001 From: ilyas Date: Tue, 18 Apr 2023 08:50:10 +0500 Subject: [PATCH 19/52] added 3.4.0 change log --- .huskyrc | 5 ----- changelog.md | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 .huskyrc diff --git a/.huskyrc b/.huskyrc deleted file mode 100644 index 73c98cc9..00000000 --- a/.huskyrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "hooks": { - "pre-commit": "npm run prettier" - } -} \ No newline at end of file diff --git a/changelog.md b/changelog.md index 1583e28d..e536a478 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ ## Started changelog after version 3.2.9 +### 3.4.0 + +- Added eager loading for list and view queries. +- Improved lazy loading behavior in GraphQL to avoid unnecessary queries. +- Added support for loading only requested fields in Sequelize. +- Added recursion support for both list and view queries. +- Added support for filtering associated data in recursive queries. + ### 3.3.0 - When sending emails fails, also throws errors in console From f253fa52d82cc2c7bdbb18d241c284c5a1dda83c Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 1 Jul 2023 15:59:53 +0500 Subject: [PATCH 20/52] Fix grammar mistakes in Changelog, tested 3.4.0 updates --- changelog.md | 6 ++-- src/database/mysql/mysql.ts | 7 +++-- src/devServer.ts | 60 +++++++++++++++++++++++++++---------- src/logger/index.ts | 2 +- src/utils/log.ts | 4 +++ 5 files changed, 58 insertions(+), 21 deletions(-) diff --git a/changelog.md b/changelog.md index e536a478..c04bbe3e 100644 --- a/changelog.md +++ b/changelog.md @@ -11,12 +11,12 @@ ### 3.3.0 - When sending emails fails, also throws errors in console -- When taking backup and provides invalid table/module name, the backup process failes with an error. +- When taking backup and provides invalid table/module name, the backup process failed with an error. - Added `extendFields` to `useModule` to extend `sequelize.model` table fields. ### 3.3.1 -- Removed support old legacy wertik framework. +- Removed old legacy wertik framework. - Now wertik can be import from `wertik-js/lib` instead of `wertik-js/lib/ndex` - Added more types support. -- Change the way mysql is initalized so that it can open doors for postgres and mongoose in future. \ No newline at end of file +- Change the way mysql is initialized so that it can open doors for postgres and mongoose in future. \ No newline at end of file diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index ea935223..44f1e087 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -2,7 +2,7 @@ import { Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" import { useMysqlDatabaseProps } from "../../types/database" import get from "lodash.get" -import { wLog, wLogWithSuccess } from "../../utils/log" +import { wLog, wLogWithError, wLogWithSuccess } from "../../utils/log" export const getAllRelationships = (dbName: String) => { return ` @@ -25,7 +25,10 @@ export const useMysqlDatabase = function (obj: useMysqlDatabaseProps) { ...get(obj, "options", {}), ...(databaseDefaultOptions as any).sql.dbInitializeOptions, }) - await sequelize.authenticate() + await sequelize.authenticate().catch((error) => { + wLogWithError("[DB] Connecting failed to database", obj.name) + process.exit(1); + }) wLogWithSuccess( `[Wertik-Mysql-Database]`, `Successfully connected to database ${obj.name}` diff --git a/src/devServer.ts b/src/devServer.ts index 8bcc5369..3e7224e9 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -15,30 +15,66 @@ wertik({ port: 1200, graphql: useGraphql(), database: { - wapgee_prod_new: useMysqlDatabase({ + ecommerce: useMysqlDatabase({ port: 3306, - name: "wapgee_prod_new", + name: "wertik", host: "localhost", password: "pass", username: "root", }), - test: useMysqlDatabase({ - username: "root", + wapgee_prod: useMysqlDatabase({ port: 3306, - password: "pass", + name: "wapgee_prod", host: "localhost", - name: "wertik", + password: "pass", + username: "root", }), }, modules: { + EcommerceShirts: useModule({ + name: "EcommerceShirts", + useDatabase: true, + database: "ecommerce", + table: "shirts", + on: function ({ belongsTo }) { + belongsTo({ + database: "ecommerce", + graphqlKey: "user", + module: "EcommerceUsers", + options: { + as: "user", + foreignKey: "user_id", + targetKey: "id", + }, + }) + } + }), + EcommerceUsers: useModule({ + name: "EcommerceUsers", + useDatabase: true, + database: "ecommerce", + table: "users", + on: function ({ hasMany }) { + hasMany({ + database: "ecommerce", + graphqlKey: "shirts", + module: "EcommerceShirts", + options: { + as: "shirts", + foreignKey: "user_id", + sourceKey: "id", + }, + }) + } + }), User: useModule({ name: "User", useDatabase: true, table: "users", - database: "wapgee_prod_new", + database: "wapgee_prod", on: function ({ hasMany }) { hasMany({ - database: "wapgee_prod_new", + database: "wapgee_prod", graphqlKey: "posts", module: "Post", options: { @@ -53,7 +89,7 @@ wertik({ name: "Post", useDatabase: true, table: "post", - database: "wapgee_prod_new", + database: "wapgee_prod", on: function ({ hasOne }) { hasOne({ module: "User", @@ -67,12 +103,6 @@ wertik({ }) }, }), - test: useModule({ - name: "Shirts", - useDatabase: true, - database: "test", - table: "shirts", - }), }, sockets: { mySockets: useWebSockets({ diff --git a/src/logger/index.ts b/src/logger/index.ts index 362b88b2..0cb65f51 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,5 +1,5 @@ import winston, { LoggerOptions } from "winston" -import { wLog, wLogWithSuccess } from "../utils/log" +import { wLogWithSuccess } from "../utils/log" /** * Creates a winston instance diff --git a/src/utils/log.ts b/src/utils/log.ts index 3cf8f4c0..921e01ef 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -10,6 +10,10 @@ export const wLogWithInfo = (info, ...params) => { console.log(chalk.blueBright(info), ...params) } +export const wLogWithError = (info, ...params) => { + console.log(chalk.redBright(info), ...params) +} + export const wLogWithSuccess = (info, ...params) => { console.log(chalk.greenBright(info), ...params) } From 99149385d5bae2a3c09cdb500b29fdeb62e1d629 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 1 Jul 2023 16:02:23 +0500 Subject: [PATCH 21/52] Update changelog.md --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index c04bbe3e..b3979084 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - Added support for loading only requested fields in Sequelize. - Added recursion support for both list and view queries. - Added support for filtering associated data in recursive queries. +- Removed moment.js and replaced with dayjs. ### 3.3.0 From 6bab14571c69dfb1d75e41bce1c0216393d2f879 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 1 Jul 2023 16:17:17 +0500 Subject: [PATCH 22/52] '[Build Optimization] Removed lodash, installed its child packages to reduce app size.' --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index b3979084..2b9a98c5 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ - Added recursion support for both list and view queries. - Added support for filtering associated data in recursive queries. - Removed moment.js and replaced with dayjs. +- Removed lodash, installed its child packages to reduce app size. ### 3.3.0 From 81e5f12b4f9bcef1254e86266296bd9b89d83078 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 1 Jul 2023 20:42:37 +0500 Subject: [PATCH 23/52] Fix sonar issues, and reduced code smells to less than 10 --- .gitignore | 4 +- docs/v3/graphql.md | 10 ++--- docs/v3/queue.md | 4 +- src/cronJobs/index.ts | 4 +- src/crud/index.ts | 12 +++--- src/crud/paginate.ts | 7 +++- src/database/database.ts | 9 +++-- src/database/eagerLoadingGraphqlQuery.ts | 3 +- src/database/helpers.ts | 19 +++++---- src/database/mysql/getTableInfo.ts | 8 ++-- src/database/mysql/mysql.ts | 8 ++-- src/devServer.ts | 4 +- src/graphql/index.ts | 6 +-- src/helpers/modules/backup.ts | 4 +- src/index.ts | 36 +++++++---------- src/logger/consoleMessages.ts | 8 +++- src/mailer/index.ts | 11 ++---- src/modules/modules.ts | 49 ++++++++++++++---------- src/modules/modulesHelpers.ts | 27 +++++++------ src/queue/index.ts | 6 +-- src/sockets/index.ts | 2 +- src/storage/index.ts | 4 +- src/store.ts | 2 +- src/types/cronJobs.ts | 8 ++-- src/types/database.ts | 4 +- src/types/graphql.ts | 2 +- src/types/index.ts | 16 ++++---- src/types/mailer.ts | 2 +- src/types/modules.ts | 32 ++++++++-------- src/types/queue.ts | 2 +- src/types/storage.ts | 2 +- src/utils/camelize.ts | 10 +++-- src/utils/checkInstalledPackages.ts | 2 +- src/utils/voidFunction.ts | 2 +- tests/index.test.js | 6 ++- 35 files changed, 172 insertions(+), 163 deletions(-) diff --git a/.gitignore b/.gitignore index 3943ceb3..84e8b914 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ warning.log .env /backups .DS_STORE -lib \ No newline at end of file +lib + +.scannerwork diff --git a/docs/v3/graphql.md b/docs/v3/graphql.md index dcac33df..19ae0980 100644 --- a/docs/v3/graphql.md +++ b/docs/v3/graphql.md @@ -6,18 +6,18 @@ For GraphQL, Wertik JS uses Apollo GraphQL under the hood. We choose Apollo Grap import wertik, { useGraphql } from "wertik-js/lib/"; wertik({ port: 1200, - graphql: useGraphql(useGraphqlProps), + graphql: useGraphql(UseGraphqlProps), }); ``` This will initialize GraphQL on URL: http://localhost:1200/graphql. If you visit this link you will Apollo GraphQL playground. -#### useGraphqlProps +#### UseGraphqlProps -useGraphqlProps is an argument which is optional when using `useGraphql` method. +UseGraphqlProps is an argument which is optional when using `useGraphql` method. ```typescript -export interface useGraphqlProps { +export interface UseGraphqlProps { options?: { [key: string]: any; }; @@ -32,5 +32,5 @@ export interface useGraphqlProps { - options includes ApolloServer options. - applyMiddlewareOptions includes options while integrating Apollo Server with express server with same port. -- resolvers for defined schema in useGraphqlProps.typeDefs. +- resolvers for defined schema in UseGraphqlProps.typeDefs. - typeDefs is your graphql schema. diff --git a/docs/v3/queue.md b/docs/v3/queue.md index eda013fe..74804abe 100644 --- a/docs/v3/queue.md +++ b/docs/v3/queue.md @@ -60,12 +60,12 @@ This `useQueue` method simply instantiates a new instance of bull and returns th - A queue can be instantiated with some useful options, for instance, we can specify the `location` and `password` of our Redis server, as well as some other useful settings. we can use them as `options : {redis : {port : 6379, host : "127.0.0.1", password : "somepass" }}`. -#### useQueueProps +#### UseQueueProps -The `useQueue` method always expects an instantiation name such as(`my-queue-name`) all the other arguments are optional. ```typescript -export interface useQueueProps { +export interface UseQueueProps { name?: string url?: string options?: QueueOptions diff --git a/src/cronJobs/index.ts b/src/cronJobs/index.ts index fa897c23..5878ac71 100644 --- a/src/cronJobs/index.ts +++ b/src/cronJobs/index.ts @@ -1,9 +1,9 @@ import get from "lodash.get" import nodeCron from "node-cron" -import { useCronJobsProps } from "../types/cronJobs" +import { UseCronJobsProps } from "../types/cronJobs" import { iObject, WertikApp, WertikConfiguration } from "../types" -export const useCronJob = (cron: useCronJobsProps) => { +export const useCronJob = (cron: UseCronJobsProps) => { return ({ configuration, wertikApp, diff --git a/src/crud/index.ts b/src/crud/index.ts index 30d67ed8..8324df58 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -38,18 +38,18 @@ export default function (module, schemaInformation, store) { return ` extend type Mutation { update${module.name}(input: update${module.name}Input,where: ${module.name}FilterInput!): ${module.name}BulkMutationResponse - create${module.name}(input: [create${module.name}Input]): ${module.name}BulkMutationResponse + insert${module.name}(input: [insert${module.name}Input]): ${module.name}BulkMutationResponse delete${module.name}(where: ${module.name}FilterInput!): SuccessResponse - createOrUpdate${module.name}(id: Int, input: create${module.name}Input): ${module.name} + InsertOrUpdate${module.name}(id: Int, input: insert${module.name}Input): ${module.name} } ` }, generateCrudResolvers() { return { Mutation: { - [`createOrUpdate${module.name}`]: get( + [`InsertOrUpdate${module.name}`]: get( module, - "graphql.mutations.createOrUpdate", + "graphql.mutations.InsertOrUpdate", async (_, args, context, info) => { wLogWithDateWithInfo( "[Wertik-GraphQL-Mutation]", @@ -57,7 +57,7 @@ export default function (module, schemaInformation, store) { ) const argsFromEvent = await get( module, - "events.beforeCreateOrUpdate", + "events.beforeInsertOrUpdate", voidFunction )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args @@ -143,7 +143,7 @@ export default function (module, schemaInformation, store) { return { message: `${module.name} Deleted` } } ), - [`create${module.name}`]: get( + [`insert${module.name}`]: get( module, "graphql.mutations.create", async (_, args, context, info) => { diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 6c1ec23c..3407d43e 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -1,6 +1,6 @@ import store from "../store" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" -import omit from "lodash.omit"; +import omit from "lodash.omit" export const paginate = async ( arg, @@ -10,7 +10,10 @@ export const paginate = async ( ) => { const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} const offset = limit * (page - 1) - const keys = [...store.database.relationships.map((c) => c.graphqlKey), ...store.graphql.graphqlKeys] + const keys = [ + ...store.database.relationships.map((c) => c.graphqlKey), + ...store.graphql.graphqlKeys, + ] let where = omit(convertFiltersIntoSequelizeObject(arg.where), keys) const { count, rows } = await tableInstance.findAndCountAll({ diff --git a/src/database/database.ts b/src/database/database.ts index eadf87ac..c753f6e0 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,8 +1,7 @@ import get from "lodash.get" import { wLog, wLogWithInfo } from "../utils/log" import { paginate } from "../crud/paginate" -import { Store } from "../types" -import { WertikApp } from "../types" +import { Store, WertikApp } from "../types" import logSymbols from "log-symbols" export const applyRelationshipsFromStoreToDatabase = async ( @@ -14,12 +13,14 @@ export const applyRelationshipsFromStoreToDatabase = async ( store.database.relationships.forEach((element) => { const currentTable = app.modules[element.currentModule].tableInstance const referencedTable = app.modules[element.referencedModule].tableInstance + const currentTableName = currentTable.getTableName() as string + const referencedTableName = referencedTable.getTableName() as string wLog( logSymbols.success, - `${currentTable.getTableName()}.${ + `${currentTableName}.${ element.type - }(${referencedTable.getTableName()}, ${JSON.stringify(element.options)})` + }(${referencedTableName}, ${JSON.stringify(element.options)})` ) // element.type will be hasOne, hasMany, belongsTo or belongsToMany diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index a45de369..a64d80b3 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -88,7 +88,6 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let include = recursion(graphqlFields) - /** * Make sure the include is required if filters are requested in root level filters. * If root level filters are not met then the response will be null. @@ -106,7 +105,7 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( c.where = convertFiltersIntoSequelizeObject(args.where[c.as]) } - return c; + return c }) return include diff --git a/src/database/helpers.ts b/src/database/helpers.ts index 2c6777a5..9cfb3e56 100644 --- a/src/database/helpers.ts +++ b/src/database/helpers.ts @@ -12,43 +12,42 @@ export const convertDatabaseTypeIntoGraphqlType = ( columnInfo: MysqlColumnInfoDescribeTable, tableName: string ) => { - var isPrimary = columnInfo.Key === "PRI" - var limit = columnInfo.Type.match(/\d+/g) - var isRequiredIndicator = columnInfo.Null === "NO" ? "!" : "" - if (limit) limit[0] + let isPrimary = columnInfo.Key === "PRI" + // let limit = columnInfo.Type.match(/\d+/g) + let isRequiredIndicator = columnInfo.Null === "NO" ? "!" : "" if (columnInfo.Type.toLowerCase() === "tinyint(1)") { return { graphqlType: `Boolean`, - graphqlCreateInputType: `Boolean${isRequiredIndicator}`, + graphqlInsertInputType: `Boolean${isRequiredIndicator}`, graphqlUpdateInputType: `Boolean${isRequiredIndicator}`, databaseType: "INTEGER", } } else if (numberTypes.find((c) => columnInfo.Type.includes(c))) { return { graphqlType: `Int`, - graphqlCreateInputType: `Int`, + graphqlInsertInputType: `Int`, graphqlUpdateInputType: `Int${isPrimary ? "!" : ""}`, databaseType: "INTEGER", } } else if (jsonTypes.find((c) => columnInfo.Type.includes(c))) { return { graphqlType: `JSON`, - graphqlCreateInputType: `String${isRequiredIndicator}`, + graphqlInsertInputType: `String${isRequiredIndicator}`, graphqlUpdateInputType: `String${isRequiredIndicator}`, databaseType: "STRING", } } else if (stringTypes.find((c) => columnInfo.Type.includes(c))) { return { graphqlType: `String`, - graphqlCreateInputType: `String${isRequiredIndicator}`, + graphqlInsertInputType: `String${isRequiredIndicator}`, graphqlUpdateInputType: `String${isRequiredIndicator}`, databaseType: "STRING", } } else if (dateTypes.find((c) => columnInfo.Type.includes(c))) { return { graphqlType: `String`, - graphqlCreateInputType: `String${isRequiredIndicator}`, + graphqlInsertInputType: `String${isRequiredIndicator}`, graphqlUpdateInputType: `String${isRequiredIndicator}`, databaseType: "STRING", isDateColumn: true, @@ -58,7 +57,7 @@ export const convertDatabaseTypeIntoGraphqlType = ( graphqlType: `${capitalizeFirstLetter(tableName)}${capitalizeFirstLetter( columnInfo.Field )}Enum`, - graphqlCreateInputType: `${capitalizeFirstLetter( + graphqlInsertInputType: `${capitalizeFirstLetter( tableName )}${capitalizeFirstLetter(columnInfo.Field)}Enum${isRequiredIndicator}`, graphqlUpdateInputType: `${capitalizeFirstLetter( diff --git a/src/database/mysql/getTableInfo.ts b/src/database/mysql/getTableInfo.ts index 7eb3aa0d..9c60a259 100644 --- a/src/database/mysql/getTableInfo.ts +++ b/src/database/mysql/getTableInfo.ts @@ -1,4 +1,4 @@ -import { useModuleProps } from "src/types/modules" +import { UseModuleProps } from "src/types/modules" import { convertDatabaseTypeIntoGraphqlType } from "../helpers" import { MysqlColumnInfoDescribeTable, TableInfo } from "./../../types/database" @@ -30,7 +30,7 @@ export const enumTypes = ["enum"] export const jsonTypes = ["json"] export const getMysqlTableInfo = async ( - module: useModuleProps, + module: UseModuleProps, sequelize: any ): Promise => { let rows = await sequelize.query(`describe ${module.table};`) @@ -44,13 +44,13 @@ export const getMysqlTableInfo = async ( element, module.name ) - var isPrimary = element.Key === "PRI" + let isPrimary = element.Key === "PRI" return { columnName: element.Field, default: element.Default, graphqlType: graphqlType.graphqlType, - graphqlCreateInputType: graphqlType.graphqlCreateInputType, + graphqlInsertInputType: graphqlType.graphqlInsertInputType, graphqlUpdateInputType: graphqlType.graphqlUpdateInputType, enumValues: graphqlType.enumValues, isNull: element.Null === "no" ? false : true, diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 44f1e087..8d3e8f48 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -1,10 +1,10 @@ import { Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" -import { useMysqlDatabaseProps } from "../../types/database" +import { UseMysqlDatabaseProps } from "../../types/database" import get from "lodash.get" import { wLog, wLogWithError, wLogWithSuccess } from "../../utils/log" -export const getAllRelationships = (dbName: String) => { +export const getAllRelationships = (dbName: string) => { return ` SELECT * FROM information_schema.KEY_COLUMN_USAGE @@ -15,7 +15,7 @@ export const getAllRelationships = (dbName: String) => { ` } -export const useMysqlDatabase = function (obj: useMysqlDatabaseProps) { +export const useMysqlDatabase = function (obj: UseMysqlDatabaseProps) { return async () => { try { let sequelize = new Sequelize(obj.name, obj.username, obj.password, { @@ -27,7 +27,7 @@ export const useMysqlDatabase = function (obj: useMysqlDatabaseProps) { }) await sequelize.authenticate().catch((error) => { wLogWithError("[DB] Connecting failed to database", obj.name) - process.exit(1); + process.exit(1) }) wLogWithSuccess( `[Wertik-Mysql-Database]`, diff --git a/src/devServer.ts b/src/devServer.ts index 3e7224e9..7bd83617 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -47,7 +47,7 @@ wertik({ targetKey: "id", }, }) - } + }, }), EcommerceUsers: useModule({ name: "EcommerceUsers", @@ -65,7 +65,7 @@ wertik({ sourceKey: "id", }, }) - } + }, }), User: useModule({ name: "User", diff --git a/src/graphql/index.ts b/src/graphql/index.ts index d0137b1b..92e41a77 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -2,10 +2,10 @@ import get from "lodash.get" import omit from "lodash.omit" import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" -import { useGraphqlProps, GraphqlInitializeProps } from "../types/graphql" -import { wLog, wLogWithInfo, wLogWithSuccess } from "../utils/log" +import { UseGraphqlProps, GraphqlInitializeProps } from "../types/graphql" +import { wLogWithSuccess } from "../utils/log" -export const useGraphql = (props?: useGraphqlProps) => { +export const useGraphql = (props?: UseGraphqlProps) => { return ({ wertikApp, expressApp, diff --git a/src/helpers/modules/backup.ts b/src/helpers/modules/backup.ts index 336ac183..93fc2733 100644 --- a/src/helpers/modules/backup.ts +++ b/src/helpers/modules/backup.ts @@ -45,7 +45,7 @@ const uploadDumpToDigitalOceanSpaces = async ( Bucket, ACL ) => { - const data = await fs.readFileSync(filename) + const data = fs.readFileSync(filename) const params = { Bucket: Bucket, @@ -59,7 +59,7 @@ const uploadDumpToDigitalOceanSpaces = async ( return response } const uploadDumpToDropbox = async (filename, dropboxInstance) => { - const data: Buffer = await fs.readFileSync(filename) + const data: Buffer = fs.readFileSync(filename) const response = await dropboxInstance.dropbox.filesUpload({ strict_conflict: false, path: `/${filename}`, diff --git a/src/index.ts b/src/index.ts index 1e207db6..6567dc9e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,10 +7,9 @@ import { } from "./database/database" import { emailSender } from "./mailer/index" import http from "http" -import { WertikConfiguration } from "./types" -import { WertikApp } from "./types" +import { WertikConfiguration, WertikApp } from "./types" import { initializeBullBoard } from "./queue/index" -import { wLog, wLogWithSuccess } from "./utils/log" +import { wLogWithSuccess } from "./utils/log" export * from "./database/database" export * from "./modules/modules" @@ -44,7 +43,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( wertikApp.express = expressApp wertikApp.port = configuration.port - if (configuration.mailer && configuration.mailer.instances) { + if (configuration?.mailer?.instances) { for (const mailName of Object.keys( configuration.mailer.instances || {} )) { @@ -54,33 +53,27 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( } } - if (configuration.storage) { + if (configuration?.storage) { for (const storageName of Object.keys(configuration.storage || {})) { - wertikApp.storage[storageName] = await configuration.storage[ - storageName - ]({ + wertikApp.storage[storageName] = configuration.storage[storageName]({ configuration: configuration, wertikApp: wertikApp, }) } } - if (configuration.cronJobs) { + if (configuration?.cronJobs) { for (const cronName of Object.keys(configuration.cronJobs || {})) { - wertikApp.cronJobs[cronName] = await configuration.cronJobs[cronName]( - { - configuration: configuration, - wertikApp: wertikApp, - } - ) + wertikApp.cronJobs[cronName] = configuration.cronJobs[cronName]({ + configuration: configuration, + wertikApp: wertikApp, + }) } } if (configuration.sockets) { for (const socketName of Object.keys(configuration.sockets || {})) { - wertikApp.sockets[socketName] = await configuration.sockets[ - socketName - ]({ + wertikApp.sockets[socketName] = configuration.sockets[socketName]({ configuration: configuration, wertikApp: wertikApp, }) @@ -113,9 +106,8 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( if (configuration?.queue?.jobs) { for (const queueName of Object.keys(configuration?.queue?.jobs || {})) { - wertikApp.queue.jobs[queueName] = await configuration.queue.jobs[ - queueName - ]() + wertikApp.queue.jobs[queueName] = + configuration.queue.jobs[queueName]() } } @@ -128,7 +120,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( if (configuration.redis) { for (const redisName of Object.keys(configuration.redis || {})) { - wertikApp.redis[redisName] = await configuration.redis[redisName]({ + wertikApp.redis[redisName] = configuration.redis[redisName]({ wertikApp, configuration, }) diff --git a/src/logger/consoleMessages.ts b/src/logger/consoleMessages.ts index d74cfb03..78d0ba14 100644 --- a/src/logger/consoleMessages.ts +++ b/src/logger/consoleMessages.ts @@ -15,6 +15,10 @@ export const errorMessage = function (message) { wLog(logSymbols.error, ` [Wertik-js]:`, chalk.red(message)) } -export const warningMessage = function () {} +export const warningMessage = function (message) { + wLog(logSymbols.warning, ` [Wertik-js]:`, chalk.yellow(message)) +} -export const infoMessage = function () {} +export const infoMessage = function (message) { + wLog(logSymbols.info, ` [Wertik-js]:`, chalk.blue(message)) +} diff --git a/src/mailer/index.ts b/src/mailer/index.ts index 4de6e7b1..978052b2 100644 --- a/src/mailer/index.ts +++ b/src/mailer/index.ts @@ -2,7 +2,7 @@ import nodemailer from "nodemailer" import handlebars from "handlebars" import { useMailerProps, WertikApp, WertikConfiguration } from "../types" import { SendEmailProps } from "../types/mailer" -import { wLog, wLogWithInfo, wLogWithSuccess } from "../utils/log" +import { wLog, wLogWithSuccess } from "../utils/log" export const useMailer = (props: useMailerProps) => { return async () => { @@ -53,10 +53,10 @@ export const emailSender = ({ html: resultTemplate, subject: props.options.subject, }) - if (emailInstance && emailInstance.messageId) { + if (emailInstance?.messageId) { wLog("Message sent: %s", emailInstance.messageId) } - if (nodemailer && nodemailer.getTestMessageUrl) { + if (nodemailer?.getTestMessageUrl) { wLog("Preview URL: %s", nodemailer.getTestMessageUrl(emailInstance)) } @@ -67,10 +67,7 @@ export const emailSender = ({ wertikApp, configuration, emailInstance, - previewURL: - nodemailer && nodemailer.getTestMessageUrl - ? nodemailer.getTestMessageUrl(emailInstance) - : "", + previewURL: nodemailer?.getTestMessageUrl(emailInstance), }) } diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 73520d75..f41bdab1 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -1,23 +1,23 @@ import get from "lodash.get" import { databaseDefaultOptions } from "../utils/defaultOptions" -import { RelationParams, useModuleProps } from "../types/modules" +import { RelationParams, UseModuleProps } from "../types/modules" import { - getCreateSchema, + getInsertSchema, getUpdateSchema, generateEnumTypeForGraphql, generateGenerateGraphQLCrud, } from "./modulesHelpers" import { getMysqlTableInfo } from "../database/mysql/getTableInfo" import { Store, WertikApp, WertikConfiguration } from "./../types/index" -import { ModelCtor, Model, ModelAttributes } from "sequelize/types" -import { wLogWithInfo } from "../utils/log" +import { ModelStatic, Model, ModelAttributes } from "sequelize/types" +import { wLogWithInfo } from "../utils/log" import camelize from "../utils/camelize" /** * Wertik js module - * @param props see interface useModuleProps + * @param props see interface UseModuleProps */ -export const useModule = (moduleProps: useModuleProps) => { +export const useModule = (moduleProps: UseModuleProps) => { return async ({ store, configuration, @@ -27,8 +27,8 @@ export const useModule = (moduleProps: useModuleProps) => { configuration: WertikConfiguration app: WertikApp }) => { - let currentModuleRelationships = []; - let tableInstance: ModelCtor> + let currentModuleRelationships = [] + let tableInstance: ModelStatic> let graphqlSchema = [`type ${moduleProps.name} {`] let listSchema = "" let filterSchema = [`input ${moduleProps.name}FilterInput {`] @@ -86,9 +86,11 @@ export const useModule = (moduleProps: useModuleProps) => { type: "hasOne", } store.database.relationships.push(relationshipInfo) - currentModuleRelationships.push(relationshipInfo); + currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) - filterSchema.push(`${camelize(params.graphqlKey)}: ${params.module}FilterInput`); + filterSchema.push( + `${camelize(params.graphqlKey)}: ${params.module}FilterInput` + ) } const belongsTo = (params: RelationParams) => { graphqlSchema.push( @@ -104,9 +106,11 @@ export const useModule = (moduleProps: useModuleProps) => { type: "belongsTo", } store.database.relationships.push(relationshipInfo) - currentModuleRelationships.push(relationshipInfo); + currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) - filterSchema.push(`${camelize(params.graphqlKey)}: ${params.module}FilterInput`); + filterSchema.push( + `${camelize(params.graphqlKey)}: ${params.module}FilterInput` + ) } const belongsToMany = (params: RelationParams) => { graphqlSchema.push( @@ -122,9 +126,11 @@ export const useModule = (moduleProps: useModuleProps) => { type: "belongsToMany", } store.database.relationships.push(relationshipInfo) - currentModuleRelationships.push(relationshipInfo); + currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) - filterSchema.push(`${camelize(params.graphqlKey)}: ${params.module}FilterInput`); + filterSchema.push( + `${camelize(params.graphqlKey)}: ${params.module}FilterInput` + ) } const hasMany = (params: RelationParams) => { graphqlSchema.push( @@ -139,10 +145,12 @@ export const useModule = (moduleProps: useModuleProps) => { options: params.options, type: "hasMany", } - currentModuleRelationships.push(relationshipInfo); + currentModuleRelationships.push(relationshipInfo) store.database.relationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) - filterSchema.push(`${camelize(params.graphqlKey)}: ${params.module}FilterInput`); + filterSchema.push( + `${camelize(params.graphqlKey)}: ${params.module}FilterInput` + ) } get(moduleProps, "on", () => {})({ useQuery, @@ -155,9 +163,10 @@ export const useModule = (moduleProps: useModuleProps) => { useSchema, }) + let insertSchema = [] + let updateSchema = [] + if (useDatabase) { - var createSchema = [] - var updateSchema = [] const connection = app.database[moduleProps.database] // info const tableInfo = await getMysqlTableInfo( @@ -207,7 +216,7 @@ export const useModule = (moduleProps: useModuleProps) => { updateSchema = getUpdateSchema(moduleProps, tableInfo) - createSchema = getCreateSchema(moduleProps, tableInfo) + insertSchema = getInsertSchema(moduleProps, tableInfo) tableInfo.columns.forEach((column) => { let filterInput = @@ -235,7 +244,7 @@ export const useModule = (moduleProps: useModuleProps) => { tableInstance: tableInstance, schema: graphqlSchema.join(`\n`), inputSchema: { - create: createSchema || "", + insert: insertSchema || "", update: updateSchema || "", list: listSchema, filters: filterSchema.join("\n"), diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 0747debb..a7535532 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -1,10 +1,9 @@ import get from "lodash.get" -import { useModuleProps } from "../types/modules" +import { UseModuleProps } from "../types/modules" import { TableInfo } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" import crud from "../crud" import store from "../store" -import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" export const generateDataTypeFromDescribeTableColumnType = (Type: string) => { let length = Type.match(/[0-9]/g)?.join("") @@ -32,7 +31,7 @@ export const getGraphQLTypeNameFromSqlType = ( }, module ) => { - var type = column.Type + let type = column.Type if (typeof column.Type === "string") { type = type.toLowerCase() } else { @@ -62,7 +61,7 @@ export const getGraphQLTypeNameFromSqlType = ( } export const getUpdateSchema = ( - module: useModuleProps, + module: UseModuleProps, tableInfo: TableInfo ) => { const optionsUpdateSchema = get(module, "graphql.updateSchema", "") @@ -80,23 +79,23 @@ export const getUpdateSchema = ( return updateSchema.join("\n") } -export const getCreateSchema = ( - module: useModuleProps, +export const getInsertSchema = ( + module: UseModuleProps, tableInfo: TableInfo ) => { - const optionsCreateSchema = get(module, "graphql.createSchema", "") - if (optionsCreateSchema) return optionsCreateSchema - let createSchema = [`input create${module.name}Input {`] + const optionsInsertSchema = get(module, "graphql.createSchema", "") + if (optionsInsertSchema) return optionsInsertSchema + let insertSchema = [`input insert${module.name}Input {`] tableInfo.columns.forEach((column) => { if (column.columnName !== "id" && !column.isDateColumn) { - createSchema.push( - `${column.columnName}: ${column.graphqlCreateInputType}` + insertSchema.push( + `${column.columnName}: ${column.graphqlInsertInputType}` ) } }) - createSchema.push("}") + insertSchema.push("}") - return createSchema.join("\n") + return insertSchema.join("\n") } export const generateEnumTypeForGraphql = (column: TableInfo["columns"][0]) => { @@ -116,7 +115,7 @@ export const generateGenerateGraphQLCrud = ( store.graphql.typeDefs = store.graphql.typeDefs.concat( `\n ${schemaInformation.schema} \n ${schemaInformation.inputSchema.filters} - \n ${schemaInformation.inputSchema.create} + \n ${schemaInformation.inputSchema.insert} \n ${schemaInformation.inputSchema.update} ` ) diff --git a/src/queue/index.ts b/src/queue/index.ts index 4ab692fa..5e131141 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -1,7 +1,7 @@ import Queue from "bull" import { isPackageInstalled } from "../utils/checkInstalledPackages" import { WertikApp, WertikConfiguration } from "../types" -import { useQueueProps } from "./../types/queue" +import { UseQueueProps } from "./../types/queue" import { wLog } from "../utils/log" /** @@ -11,7 +11,7 @@ import { wLog } from "../utils/log" * @returns Queue */ -export const useQueue = (props: useQueueProps) => { +export const useQueue = (props: UseQueueProps) => { return () => new Queue(props.name, props.url, props.options) } @@ -19,7 +19,7 @@ export const initializeBullBoard = (props: { wertikApp: WertikApp configuration: WertikConfiguration }) => { - var isInstalledBullBoardExpress = isPackageInstalled("@bull-board/express") + let isInstalledBullBoardExpress = isPackageInstalled("@bull-board/express") if (!isInstalledBullBoardExpress) { throw new Error( "Please install package @bull-board/express to initialize Bull Board for Queue" diff --git a/src/sockets/index.ts b/src/sockets/index.ts index ab509281..bad09f00 100644 --- a/src/sockets/index.ts +++ b/src/sockets/index.ts @@ -2,7 +2,7 @@ import { Server as SocketIOServer, ServerOptions as SocketIOServerOptions, } from "socket.io" -import { wLog, wLogWithSuccess } from "../utils/log" +import { wLogWithSuccess } from "../utils/log" import { Server as WebSocketServer, ServerOptions as WebSocketServerOptions, diff --git a/src/storage/index.ts b/src/storage/index.ts index 4a901c05..1dedb68d 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -1,11 +1,11 @@ import { WertikApp, WertikConfiguration } from "../types" -import { useStorageProps } from "../types/storage" +import { UseStorageProps } from "../types/storage" import { wLog } from "../utils/log" const DIGITAL_OCEAN = "digitalocean" const DROPBOX = "dropbox" -export const useStorage = (storageItem: useStorageProps) => { +export const useStorage = (storageItem: UseStorageProps) => { return ({ configuration, wertikApp, diff --git a/src/store.ts b/src/store.ts index d2f65eef..b1bf1bfb 100644 --- a/src/store.ts +++ b/src/store.ts @@ -22,7 +22,7 @@ export const wertikApp: WertikApp = { const store: { graphql: { - graphqlKeys: string[], + graphqlKeys: string[] typeDefs: string resolvers: { Query: { diff --git a/src/types/cronJobs.ts b/src/types/cronJobs.ts index 090d968f..1099be9a 100644 --- a/src/types/cronJobs.ts +++ b/src/types/cronJobs.ts @@ -1,9 +1,9 @@ import { WertikApp } from "." -export interface useCronJobsProps { +export interface UseCronJobsProps { expression: string name: string - beforeRun?: (app: WertikApp) => void | any - afterRun?: (app: WertikApp) => void | any - handler: (app: WertikApp) => void | any + beforeRun?: (app: WertikApp) => void + afterRun?: (app: WertikApp) => void + handler: (app: WertikApp) => void } diff --git a/src/types/database.ts b/src/types/database.ts index 365a6345..0bbcbbde 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -1,6 +1,6 @@ import { iObject } from "." -export interface useMysqlDatabaseProps { +export interface UseMysqlDatabaseProps { /** * Database name */ @@ -37,7 +37,7 @@ export interface TableInfo { default: string | number extra: any graphqlType: string - graphqlCreateInputType: string + graphqlInsertInputType: string graphqlUpdateInputType: string enumValues: string[] | null isEnum: boolean diff --git a/src/types/graphql.ts b/src/types/graphql.ts index 71ce4bbe..68e9c850 100644 --- a/src/types/graphql.ts +++ b/src/types/graphql.ts @@ -5,7 +5,7 @@ export interface GetMiddlewareOptionsGraphql extends GetMiddlewareOptions { path: string } -export interface useGraphqlProps { +export interface UseGraphqlProps { options?: { [key: string]: any } diff --git a/src/types/index.ts b/src/types/index.ts index 22803ff6..1d753594 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ import { Sequelize } from "sequelize/types" -import { useMysqlDatabaseProps } from "./database" +import { UseMysqlDatabaseProps } from "./database" import { SendEmailProps } from "./mailer" import { WertikModule } from "./modules" @@ -50,7 +50,7 @@ export interface WertikConfiguration { */ database?: { [key: string]: () => Promise<{ - credentials: useMysqlDatabaseProps + credentials: UseMysqlDatabaseProps instance: Sequelize }> } @@ -86,7 +86,7 @@ export interface WertikConfiguration { } events?: { /** - * Runs when email sents successfully. + * Runs when email sent successfully. */ onEmailSent?: (options: { options: iObject @@ -94,18 +94,18 @@ export interface WertikConfiguration { configuration: WertikConfiguration emailInstance: any previewURL: string | boolean - mailer: String - }) => void | any | null | undefined + mailer: string + }) => void /** * Runs when email fails to send. */ onEmailSentFailed?: (options: { - mailer: String + mailer: string wertikApp: WertikApp configuration: WertikConfiguration error: any options: iObject - }) => void | any | null | undefined + }) => void } } /** @@ -215,7 +215,7 @@ export interface useMailerProps { */ name: string /** - * Provide options that you provide procide to nodemailer.createTransport function. + * Provide options that you provide provide to nodemailer.createTransport function. */ options?: { [key: string]: any diff --git a/src/types/mailer.ts b/src/types/mailer.ts index 337c435f..55c6d401 100644 --- a/src/types/mailer.ts +++ b/src/types/mailer.ts @@ -9,4 +9,4 @@ export interface SendEmailProps { [key: string]: any } -export interface userMailerProps {} +export interface UserMailerProps {} diff --git a/src/types/modules.ts b/src/types/modules.ts index adbb4f5d..681655a5 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -1,7 +1,7 @@ import { iObject } from "." -import { ModelCtor, Model } from "sequelize/types" +import { ModelStatic, Model } from "sequelize/types" -export interface useQueryProps { +export interface UseQueryProps { /** * Schema for this query, for example: getUsers: [Users] */ @@ -15,7 +15,7 @@ export interface useQueryProps { */ name: string } -export interface useMutationProps { +export interface UseMutationProps { /** * Schema for this query, for example: deleteUsers: Boolean */ @@ -38,9 +38,9 @@ export interface RelationParams { [key: string]: string | number | null } } -export type useSchemaProps = string +export type UseSchemaProps = string -export interface useModuleProps { +export interface UseModuleProps { /** * Your module name. */ @@ -83,7 +83,7 @@ export interface useModuleProps { /** * Wertik-js creates create a schema from the database table. Once defined this, Wertik JS will ignore creating create a schema from the table information. */ - createSchema: string + insertSchema: string mutations?: { /** * Overrides default behavior of updating a record from the database table. @@ -96,11 +96,11 @@ export interface useModuleProps { /** * Overrides default behavior of create a record from the database table. */ - create?: Function + insert?: Function /** * Overrides default behavior of creating or updating a record from the database table. */ - createOrUpdate?: Function + insertOrUpdate?: Function } } /** @@ -110,11 +110,11 @@ export interface useModuleProps { /** * This Method allows you adding graphql query to your module. */ - useQuery: (props: useQueryProps) => {} | void + useQuery: (props: UseQueryProps) => {} | void /** * This Method allows you adding graphql mutation to your module. */ - useMutation: (props: useMutationProps) => {} | void + useMutation: (props: UseMutationProps) => {} | void /** * This method gives you access to express app instance. */ @@ -138,7 +138,7 @@ export interface useModuleProps { /** * This method adds has many relationship to a module. */ - useSchema: (props: useSchemaProps) => {} | void + useSchema: (props: UseSchemaProps) => {} | void }) => void /** * Graphql events when a CRUD operation happens. @@ -159,7 +159,7 @@ export interface useModuleProps { /** * This events runs before beforeCreate mutation. */ - beforeCreate?: Function + beforeInsert?: Function /** * This events runs before beforeDelete mutation. */ @@ -169,17 +169,17 @@ export interface useModuleProps { */ beforeUpdate?: Function /** - * This events runs before beforeCreateOrUpdate mutation. + * This events runs before beforeInsertOrUpdate mutation. */ - beforeCreateOrUpdate?: Function + beforeInsertOrUpdate?: Function } } export interface WertikModule { - tableInstance: ModelCtor> + tableInstance: ModelStatic> schema: string inputSchema: { - create: string | any[] + insert: string | any[] update: string | any[] list: string filters: string diff --git a/src/types/queue.ts b/src/types/queue.ts index 3511f7f2..473650c4 100644 --- a/src/types/queue.ts +++ b/src/types/queue.ts @@ -1,6 +1,6 @@ import { QueueOptions } from "bull" -export interface useQueueProps { +export interface UseQueueProps { name: string url?: string options?: QueueOptions diff --git a/src/types/storage.ts b/src/types/storage.ts index 983df9b6..f70a4faa 100644 --- a/src/types/storage.ts +++ b/src/types/storage.ts @@ -1,4 +1,4 @@ -export interface useStorageProps { +export interface UseStorageProps { for: "dropbox" | "digitalocean" name: string dropboxOptions?: { diff --git a/src/utils/camelize.ts b/src/utils/camelize.ts index b3b64a47..ad8548ba 100644 --- a/src/utils/camelize.ts +++ b/src/utils/camelize.ts @@ -1,7 +1,9 @@ function camelize(str) { - return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word, index) { - return index === 0 ? word.toLowerCase() : word.toUpperCase(); - }).replace(/\s+/g, ''); + return str + .replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) { + return index === 0 ? word.toLowerCase() : word.toUpperCase() + }) + .replace(/\s+/g, "") } -export default camelize \ No newline at end of file +export default camelize diff --git a/src/utils/checkInstalledPackages.ts b/src/utils/checkInstalledPackages.ts index 29273e18..62f27b7f 100644 --- a/src/utils/checkInstalledPackages.ts +++ b/src/utils/checkInstalledPackages.ts @@ -15,7 +15,7 @@ export function isPackageInstalled(packageName) { } } -export function check(name: String) { +export function check(name: string) { const isInstalled = isPackageInstalled(name) if (isInstalled) { return true diff --git a/src/utils/voidFunction.ts b/src/utils/voidFunction.ts index 8349ba35..9978b959 100644 --- a/src/utils/voidFunction.ts +++ b/src/utils/voidFunction.ts @@ -1 +1 @@ -export const voidFunction = function () {} \ No newline at end of file +export const voidFunction = () => 1 + 2 === 1 diff --git a/tests/index.test.js b/tests/index.test.js index 73e20582..283b2c76 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -22,7 +22,7 @@ const database = { } test("Expect no configuration can start the server", async () => { - await expect((app = wertik())).resolves.not.toThrowError() + await expect((wertik())).resolves.not.toThrowError() }) test("Expect empty configuration object an start the server", async () => { @@ -86,7 +86,7 @@ test("Expect graphql to work with useGraphql and does not causes error", async ( ).resolves.not.toThrowError() }) -test("Expect useWebsockets, useIndependentWebSocketsServer and useSocketIO works and does not throw any error", async () => { +test("Expect useWebSockets, useIndependentWebSocketsServer and useSocketIO works and does not throw any error", async () => { await expect( wertik({ sockets: { @@ -100,6 +100,8 @@ test("Expect useWebsockets, useIndependentWebSocketsServer and useSocketIO works port: 1500, }), }, + }).then((app) => { + app.sockets.mySockets2.close() }) ).resolves.not.toThrowError() }) From 3735e2609de4b60c704487887c06546cfc130da9 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 23 Jul 2023 05:50:13 +0500 Subject: [PATCH 24/52] Types renaming and fixing typos --- docs/v3/redis.md | 6 +++--- src/database/eagerLoadingGraphqlQuery.ts | 5 ----- src/mailer/index.ts | 4 ++-- src/redis/index.ts | 4 ++-- src/types/index.ts | 4 ++-- src/types/modules.ts | 3 +-- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/docs/v3/redis.md b/docs/v3/redis.md index 11313eaf..c815a32e 100644 --- a/docs/v3/redis.md +++ b/docs/v3/redis.md @@ -1,11 +1,11 @@ # Redis(Beta) -Wertik JS allows a using redis, Wertik JS uses package named `redis(options: useRedisProps)`. Wertik JS gives a function called `useRedis` which allows creating a redis server. Let's create a redis client: +Wertik JS allows a using redis, Wertik JS uses package named `redis(options: UseRedisProps)`. Wertik JS gives a function called `useRedis` which allows creating a redis server. Let's create a redis client: -Where `useRedisProps` is: +Where `UseRedisProps` is: ```typescript -export interface useRedisProps { +export interface UseRedisProps { [key: string]: any name: string; } diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index a64d80b3..0b2c597d 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -5,11 +5,6 @@ import has from "lodash.has" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelpers" -const getModuleNameFromKey = (key: string) => { - return store.database.relationships.find((c) => c.graphqlKey === key) - .referencedModule -} - const clean = (cleanObject) => { let recursion = (_obj) => { Object.keys(_obj).forEach((key) => { diff --git a/src/mailer/index.ts b/src/mailer/index.ts index 978052b2..f1974349 100644 --- a/src/mailer/index.ts +++ b/src/mailer/index.ts @@ -1,10 +1,10 @@ import nodemailer from "nodemailer" import handlebars from "handlebars" -import { useMailerProps, WertikApp, WertikConfiguration } from "../types" +import { UseMailerProps, WertikApp, WertikConfiguration } from "../types" import { SendEmailProps } from "../types/mailer" import { wLog, wLogWithSuccess } from "../utils/log" -export const useMailer = (props: useMailerProps) => { +export const useMailer = (props: UseMailerProps) => { return async () => { let testAccount = props.options ? null diff --git a/src/redis/index.ts b/src/redis/index.ts index 00f32208..4335548c 100644 --- a/src/redis/index.ts +++ b/src/redis/index.ts @@ -1,8 +1,8 @@ import { createClient } from "redis" import { wLog, wLogWithSuccess } from "../utils/log" -import { useRedisProps, WertikApp, WertikConfiguration } from "../types" +import { UseRedisProps, WertikApp, WertikConfiguration } from "../types" -export const useRedis = (props?: useRedisProps) => { +export const useRedis = (props?: UseRedisProps) => { return async ({ configuration, wertikApp, diff --git a/src/types/index.ts b/src/types/index.ts index 1d753594..8a3b308a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -204,12 +204,12 @@ export interface WertikApp { /** * Provide same options that redis createClient method requires. */ -export interface useRedisProps { +export interface UseRedisProps { [key: string]: any name: string } -export interface useMailerProps { +export interface UseMailerProps { /** * Provide name for your mailer. */ diff --git a/src/types/modules.ts b/src/types/modules.ts index 681655a5..e518c51b 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -38,7 +38,6 @@ export interface RelationParams { [key: string]: string | number | null } } -export type UseSchemaProps = string export interface UseModuleProps { /** @@ -138,7 +137,7 @@ export interface UseModuleProps { /** * This method adds has many relationship to a module. */ - useSchema: (props: UseSchemaProps) => {} | void + useSchema: (props: string) => {} | void }) => void /** * Graphql events when a CRUD operation happens. From 4e75e608c904a14a07c5326bb1d5726689d9dc66 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 23 Jul 2023 11:41:30 +0500 Subject: [PATCH 25/52] Added startServer(), restartServer(), stopServer() methods --- src/devServer.ts | 4 ++++ src/index.ts | 43 +++++++++++++++++++++++++++++++++++++------ src/store.ts | 3 +++ src/types/index.ts | 3 +++ 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/devServer.ts b/src/devServer.ts index 7bd83617..e22dfb5b 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -137,4 +137,8 @@ wertik({ name: "testRedis", }), }, +}).then((app) => { + setTimeout(() => { + app.stopServer() + }, 5000) }) diff --git a/src/index.ts b/src/index.ts index 6567dc9e..8637d74f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ import { emailSender } from "./mailer/index" import http from "http" import { WertikConfiguration, WertikApp } from "./types" import { initializeBullBoard } from "./queue/index" -import { wLogWithSuccess } from "./utils/log" +import { wLogWithInfo, wLogWithSuccess } from "./utils/log" export * from "./database/database" export * from "./modules/modules" @@ -164,17 +164,48 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( next() }) + let startServer = () => { + httpServer.listen(port, () => { + wLogWithSuccess(`[Wertik-App]`, `http://localhost:${port}`) + }) + } + + let stopServer = () => { + wLogWithInfo(`[Wertik-App]`, `Stopping server`) + httpServer.close(() => { + wLogWithSuccess(`[Wertik-App]`, `Server stopped`) + process.exit() + }) + } + + let restartServer = () => { + wLogWithInfo(`[Wertik-App]`, `Restarting server`) + httpServer.close(() => { + setTimeout(() => { + startServer() + }, 500) + }) + } + if (!new Object(process.env).hasOwnProperty("TEST_MODE")) { setTimeout(async () => { if (skip === false) { - httpServer.listen(port, () => { - wLogWithSuccess(`[Wertik-App]`, `http://localhost:${port}`) - }) + startServer() } - resolve(wertikApp) + resolve({ + ...wertikApp, + restartServer, + stopServer, + startServer, + }) }, 500) } else { - resolve(wertikApp) + resolve({ + ...wertikApp, + restartServer, + stopServer, + startServer, + }) } } catch (e) { console.error(e) diff --git a/src/store.ts b/src/store.ts index b1bf1bfb..c50b45f5 100644 --- a/src/store.ts +++ b/src/store.ts @@ -2,6 +2,9 @@ import generalSchema from "./graphql/generalSchema" import { WertikApp } from "./types" export const wertikApp: WertikApp = { + restartServer: () => {}, + stopServer: () => {}, + startServer: () => {}, appEnv: "local", port: 1200, modules: {}, diff --git a/src/types/index.ts b/src/types/index.ts index 8a3b308a..5e848448 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -170,6 +170,9 @@ export interface WertikConfiguration { export interface WertikApp { appEnv: "production" | "development" | "local" sendEmail?: (options: { mailer: string; options: SendEmailProps }) => iObject + restartServer: () => void + stopServer: () => void + startServer: () => void port: number modules: { [key: string]: WertikModule From cbfca0d9e28a746ad69a6b97b76a71af784ad320 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 23 Jul 2023 11:50:47 +0500 Subject: [PATCH 26/52] Allow restart server to just restart server not the new configuration --- src/devServer.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/devServer.ts b/src/devServer.ts index e22dfb5b..7bd83617 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -137,8 +137,4 @@ wertik({ name: "testRedis", }), }, -}).then((app) => { - setTimeout(() => { - app.stopServer() - }, 5000) }) From bbe8333f808760f94f971c822ad7bd107cc89939 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 23 Jul 2023 12:00:13 +0500 Subject: [PATCH 27/52] Added test database --- .npmignore | 4 +++- test_database.sql | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test_database.sql diff --git a/.npmignore b/.npmignore index 14545d6f..6b81d89d 100644 --- a/.npmignore +++ b/.npmignore @@ -17,4 +17,6 @@ warning.log uploads backups docs -tests \ No newline at end of file +tests + +test_database.sql \ No newline at end of file diff --git a/test_database.sql b/test_database.sql new file mode 100644 index 00000000..0699a19e --- /dev/null +++ b/test_database.sql @@ -0,0 +1,55 @@ +-- ------------------------------------------------------------- +-- TablePlus 5.3.8(500) +-- +-- https://tableplus.com/ +-- +-- Database: wertik +-- Generation Time: 2023-07-23 11:58:10.1740 +-- ------------------------------------------------------------- + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- Create database wertik if not exists +CREATE DATABASE IF NOT EXISTS `wertik` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */; + + +DROP TABLE IF EXISTS `shirts`; +CREATE TABLE `shirts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `sizes` enum('lg','sm','xl','xxl','xxxl') DEFAULT NULL, + `user_id` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; + +DROP TABLE IF EXISTS `users`; +CREATE TABLE `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(10) DEFAULT NULL, + `email` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1; + +INSERT INTO `shirts` (`id`, `sizes`, `user_id`) VALUES +(1, 'xl', 1); + +INSERT INTO `users` (`id`, `name`, `email`) VALUES +(12, '123', 'ads'), +(13, '123', 'ads'); + + + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; \ No newline at end of file From 6e091cd7c8199e8ff1aee523f08236b9e61589bd Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 29 Jul 2023 22:06:40 +0500 Subject: [PATCH 28/52] v3.4 added tests to ensure graphql operations work perfectly. --- src/store.ts | 2 +- src/types/index.ts | 5 +- tests/database_and_graphql.test.js | 125 +++++++++++++++++++++++++++++ tests/index.test.js | 45 +---------- 4 files changed, 130 insertions(+), 47 deletions(-) create mode 100644 tests/database_and_graphql.test.js diff --git a/src/store.ts b/src/store.ts index c50b45f5..62f549df 100644 --- a/src/store.ts +++ b/src/store.ts @@ -11,7 +11,7 @@ export const wertikApp: WertikApp = { models: {}, database: {}, mailer: {}, - graphql: {}, + graphql: null, sockets: {}, cronJobs: {}, storage: {}, diff --git a/src/types/index.ts b/src/types/index.ts index 5e848448..26a92b21 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -2,6 +2,7 @@ import { Sequelize } from "sequelize/types" import { UseMysqlDatabaseProps } from "./database" import { SendEmailProps } from "./mailer" import { WertikModule } from "./modules" +import { ApolloServer } from "apollo-server-express" export type iObject = { [key: string]: any } @@ -125,7 +126,7 @@ export interface WertikConfiguration { configuration: WertikConfiguration wertikApp: WertikApp expressApp: any - }) => iObject + }) => ApolloServer /** * Cron Jobs */ @@ -193,7 +194,7 @@ export interface WertikApp { [key: string]: WertikModule["tableInstance"] } mailer: iObject - graphql: iObject + graphql: ApolloServer sockets: iObject cronJobs: iObject storage: iObject diff --git a/tests/database_and_graphql.test.js b/tests/database_and_graphql.test.js new file mode 100644 index 00000000..302a8807 --- /dev/null +++ b/tests/database_and_graphql.test.js @@ -0,0 +1,125 @@ +require("dotenv").config() + +const { + default: wertik, + useModule, + useMysqlDatabase, + useGraphql, +} = require("../lib/index") + +const database = { + name: process.env.TEST_DATABASE_NAME, + host: process.env.TEST_DATABASE_HOST, + password: process.env.TEST_DATABASE_PASSWORD, + port: process.env.TEST_DATABASE_PORT, + username: process.env.TEST_DATABASE_USERNAME, +} + +const shirtModule = { + name: "Shirts", + useDatabase: true, + database: "default", + table: process.env.TEST_DATABASE_TABLE, +} + +if (database.name) { + test("Expect test database to connect and does not causes error", async () => { + await expect( + wertik({ + database: { + default: useMysqlDatabase(database), + }, + }) + ).resolves.not.toThrowError() + }) +} + +if (database.name) { + describe("Expect useMysqlDatabase, useModule and useGraphql, and expect module graphql operations work", () => { + let app + test("Expect test database to connect and does not causes error", async () => { + await expect( + (app = wertik({ + database: { + default: useMysqlDatabase(database), + }, + modules: { + shirt: useModule(shirtModule), + }, + graphql: useGraphql(), + }).then((wertikApp) => { + app = wertikApp + })) + ).resolves.not.toThrowError() + }) + + let testItem = null + + test("Expect graphql to insert data", async () => { + // describe create works + testItem = await app.graphql.executeOperation({ + query: ` + mutation { + insertShirts(input: { + sizes: lg + user_id: 12 + }) { + returning { + id + user_id + sizes + } + } + } + `, + }) + expect(testItem.data.insertShirts.returning[0].id).toBeGreaterThan(0) + expect(testItem.data.insertShirts.returning[0].sizes).toBe("lg") + }) + // update + test(`Expect graphql to update data`, async () => { + let updatedItem = await app.graphql.executeOperation({ + query: ` + mutation { + updateShirts(input: { sizes: xxxl}, where: { id: { _eq: ${testItem.data.insertShirts.returning[0].id} } }) { + returning { + id + sizes + } + } + } + `, + }) + expect(updatedItem.data.updateShirts.returning[0].id).toBeGreaterThan(0) + expect(updatedItem.data.updateShirts.returning[0].sizes).toBe("xxxl") + }) + // view + test("Expect graphql to view data", async () => { + let viewItem = await app.graphql.executeOperation({ + query: ` + query { + viewShirts(where: { sizes: xxxl }) { + id + sizes + } + } + + `, + }) + expect(viewItem.data.viewShirts.sizes).toBe("xxxl") + }) + // deleted + test("Expect graphql to delete data", async () => { + let deletedItem = await app.graphql.executeOperation({ + query: ` + mutation { + deleteShirts(where: { id: { _eq: ${testItem.data.insertShirts.returning[0].id} } }) { + message + } + } + `, + }) + expect(deletedItem.data.deleteShirts.message.length).toBeGreaterThan(0) + }) + }) +} diff --git a/tests/index.test.js b/tests/index.test.js index 283b2c76..1f0d3096 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -2,10 +2,8 @@ require("dotenv").config() const { default: wertik, - useModule, useLogger, useWinstonTransport, - useMysqlDatabase, useIndependentWebSocketsServer, useSocketIO, useWebSockets, @@ -13,16 +11,8 @@ const { useGraphql, } = require("./../lib/index") -const database = { - name: process.env.TEST_DATABASE_NAME, - host: process.env.TEST_DATABASE_HOST, - password: process.env.TEST_DATABASE_PASSWORD, - port: process.env.TEST_DATABASE_PORT, - username: process.env.TEST_DATABASE_USERNAME, -} - test("Expect no configuration can start the server", async () => { - await expect((wertik())).resolves.not.toThrowError() + await expect(wertik()).resolves.not.toThrowError() }) test("Expect empty configuration object an start the server", async () => { @@ -33,39 +23,6 @@ test("Expect null configuration does not causes error", async () => { await expect(wertik(null)).resolves.not.toThrowError() }) -if (database.name) { - test("Expect test database to connect and does not causes error", async () => { - await expect( - wertik({ - database: { - default: useMysqlDatabase(database), - }, - }) - ).resolves.not.toThrowError() - }) -} - -if (database.name) { - test("Expect useMysqlDatabase, useModule and useGraphql", async () => { - await expect( - wertik({ - database: { - default: useMysqlDatabase(database), - }, - modules: { - test: useModule({ - name: "Shirts", - useDatabase: true, - database: "default", - table: process.env.TEST_DATABASE_TABLE, - }), - }, - graphql: useGraphql(), - }) - ).resolves.not.toThrowError() - }) -} - test("Expect mailer to work without configuration and does not causes error", async () => { await expect( wertik({ From 456c869276e2f91ec3e214073b24c88e719ceacf Mon Sep 17 00:00:00 2001 From: ilyas Date: Sat, 29 Jul 2023 22:47:51 +0500 Subject: [PATCH 29/52] Update changelog.md --- changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.md b/changelog.md index 2b9a98c5..7885a545 100644 --- a/changelog.md +++ b/changelog.md @@ -8,7 +8,10 @@ - Added recursion support for both list and view queries. - Added support for filtering associated data in recursive queries. - Removed moment.js and replaced with dayjs. +- Scanned and verified fixes with SonarQube. - Removed lodash, installed its child packages to reduce app size. +- Added `startServer()`, `restartServer()`, `stopServer()` methods +- Added tests to ensure graphql operations work perfectly. ### 3.3.0 From 2af5667e8063af7c9fecaf22e23709170098967e Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 30 Jul 2023 20:53:55 +0500 Subject: [PATCH 30/52] v3.4 Removed where from hasOne and belongsTo query because using filter doesn't sense. --- src/crud/paginate.ts | 1 + src/database/eagerLoadingGraphqlQuery.ts | 36 ++++++++++++++++++++++++ src/modules/modules.ts | 4 +-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 3407d43e..9a510bd3 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -8,6 +8,7 @@ export const paginate = async ( includes: any[] = [], queryOptions: { [key: string]: any } = {} ) => { + console.log(includes) const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} const offset = limit * (page - 1) const keys = [ diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 0b2c597d..7f0dc747 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -42,6 +42,42 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let recursion = (_obj) => { let includes = [] + /** + * TODO: Check why setting require false on this query works, but setting true fails. + * { + listEcommerceUsers { + list { + id + name + shirts { + list { + id + sizes + user { + id + name + } + } + } + } + } + } + */ + + /** + * TODO: Check why setting require true on this query works, but setting false fails. + * { + listEcommerceShirts { + list { + id + user { + id + } + } + } + } + */ + for (const key in _obj) { if (keys.includes(key)) { const includeParams: { [key: string]: any } = { diff --git a/src/modules/modules.ts b/src/modules/modules.ts index f41bdab1..c8c2c838 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -74,7 +74,7 @@ export const useModule = (moduleProps: UseModuleProps) => { const hasOne = (params: RelationParams) => { graphqlSchema.push( - `${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}` + `${params.graphqlKey}: ${params.module}` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -94,7 +94,7 @@ export const useModule = (moduleProps: UseModuleProps) => { } const belongsTo = (params: RelationParams) => { graphqlSchema.push( - `${params.graphqlKey}(where: ${params.module}FilterInput): ${params.module}` + `${params.graphqlKey}: ${params.module}` ) let relationshipInfo = { currentModule: moduleProps.name, From 081dd35afb68a153dbdfc473ebd8fcb4de12b852 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 30 Jul 2023 21:28:20 +0500 Subject: [PATCH 31/52] v3.4 reset required fix --- src/crud/paginate.ts | 1 - src/database/eagerLoadingGraphqlQuery.ts | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 9a510bd3..3407d43e 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -8,7 +8,6 @@ export const paginate = async ( includes: any[] = [], queryOptions: { [key: string]: any } = {} ) => { - console.log(includes) const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} const offset = limit * (page - 1) const keys = [ diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 7f0dc747..79549978 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -44,7 +44,8 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( /** * TODO: Check why setting require false on this query works, but setting true fails. - * { + * + { listEcommerceUsers { list { id @@ -66,7 +67,7 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( /** * TODO: Check why setting require true on this query works, but setting false fails. - * { + { listEcommerceShirts { list { id From 068db1601c55eeff7d4644134885994c614c900c Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 30 Jul 2023 21:36:59 +0500 Subject: [PATCH 32/52] Todo comment remove fix --- test_database.sql | 50 ++++++++++++----------------------------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/test_database.sql b/test_database.sql index 0699a19e..8d060611 100644 --- a/test_database.sql +++ b/test_database.sql @@ -1,55 +1,29 @@ --- ------------------------------------------------------------- --- TablePlus 5.3.8(500) --- --- https://tableplus.com/ --- --- Database: wertik --- Generation Time: 2023-07-23 11:58:10.1740 --- ------------------------------------------------------------- - - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8mb4 */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - -- Create database wertik if not exists CREATE DATABASE IF NOT EXISTS `wertik` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */; +USE `wertik`; DROP TABLE IF EXISTS `shirts`; -CREATE TABLE `shirts` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `sizes` enum('lg','sm','xl','xxl','xxxl') DEFAULT NULL, - `user_id` bigint(20) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; - DROP TABLE IF EXISTS `users`; + CREATE TABLE `users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, + `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1; -INSERT INTO `shirts` (`id`, `sizes`, `user_id`) VALUES -(1, 'xl', 1); +CREATE TABLE `shirts` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `sizes` enum('lg','sm','xl','xxl','xxxl') DEFAULT NULL, + `user_id` bigint(20), + PRIMARY KEY (`id`), + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; INSERT INTO `users` (`id`, `name`, `email`) VALUES (12, '123', 'ads'), (13, '123', 'ads'); - - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; \ No newline at end of file +INSERT INTO `shirts` (`id`, `sizes`, `user_id`) VALUES +(1, 'xl', 12); From ce35b404609ea530a32264f5afdd192e99b80145 Mon Sep 17 00:00:00 2001 From: ilyas Date: Sun, 30 Jul 2023 21:37:59 +0500 Subject: [PATCH 33/52] Todo comment remove fix --- src/database/eagerLoadingGraphqlQuery.ts | 37 ------------------------ 1 file changed, 37 deletions(-) diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 79549978..0b2c597d 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -42,43 +42,6 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let recursion = (_obj) => { let includes = [] - /** - * TODO: Check why setting require false on this query works, but setting true fails. - * - { - listEcommerceUsers { - list { - id - name - shirts { - list { - id - sizes - user { - id - name - } - } - } - } - } - } - */ - - /** - * TODO: Check why setting require true on this query works, but setting false fails. - { - listEcommerceShirts { - list { - id - user { - id - } - } - } - } - */ - for (const key in _obj) { if (keys.includes(key)) { const includeParams: { [key: string]: any } = { From da7840f0bb4cce7f73d411a634ca08f872889102 Mon Sep 17 00:00:00 2001 From: ilyas Date: Thu, 28 Sep 2023 05:30:59 +0500 Subject: [PATCH 34/52] v3.4 updates --- .gitignore | 1 + changelog.md | 6 ++++ package.json | 2 ++ src/crud/index.ts | 25 +++++++++-------- src/crud/paginate.ts | 5 ++-- src/database/database.ts | 53 ----------------------------------- src/database/mysql/mysql.ts | 5 +++- src/devServer.ts | 8 ++++-- src/graphql/index.ts | 8 ++++++ src/index.ts | 2 -- src/modules/modules.ts | 10 ++++--- src/modules/modulesHelpers.ts | 11 ++++++++ src/types/graphql.ts | 1 + 13 files changed, 61 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index 84e8b914..64418763 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ warning.log lib .scannerwork +graphqlSchema.graphql diff --git a/changelog.md b/changelog.md index 7885a545..13ad7674 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,12 @@ - Removed lodash, installed its child packages to reduce app size. - Added `startServer()`, `restartServer()`, `stopServer()` methods - Added tests to ensure graphql operations work perfectly. +- Changed graphql list field `list` to `rows`. +- Added `Module` after module name in graphql types. For example, `User` is now `UserModule`. +- Changed list query to `users` instead of `listUsers`, `EcommerceUsers` becomes `ecommerce_users`. +- Changed view query to singular `user` instead of `viewUser`, `EcommerceUser` becomes `ecommerce_user`. +- Removed method `applyRelationshipsFromStoreToGraphql`, since we are moving to eager loading. +- Added option to output graphql type definitions in a file. ### 3.3.0 diff --git a/package.json b/package.json index 4955cf9d..8b3f1d31 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "lodash.has": "^4.5.2", "lodash.isplainobject": "^4.0.6", "lodash.omit": "^4.5.0", + "lodash.snakecase": "^4.1.1", "log-symbols": "^3.0.0", "morgan": "^1.9.1", "multer": "^1.4.2", @@ -61,6 +62,7 @@ "mysqldump": "^3.2.0", "node-cron": "^2.0.3", "nodemailer": ">=6.4.16", + "pluralize": "^8.0.0", "redis": "^4.0.1", "sequelize": "^6.3.5", "socket.io": "^4.1.3", diff --git a/src/crud/index.ts b/src/crud/index.ts index 8324df58..e33405aa 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,7 +1,7 @@ import get from "lodash.get" import { wLogWithDateWithInfo } from "../utils/log" import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" -import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelpers" +import { generateRequestedFieldsFromGraphqlInfo, generateRowFieldNameForModuleName, generateRowsFieldNameForModuleName } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import graphqlFields from "graphql-fields" import { paginate } from "./paginate" @@ -9,19 +9,22 @@ import omit from "lodash.omit" import { voidFunction } from "../utils/voidFunction" export default function (module, schemaInformation, store) { + + let rowsFieldName = generateRowsFieldNameForModuleName(module.name) + let singleRowFieldName = generateRowFieldNameForModuleName(module.name) + return { graphql: { generateQueriesCrudSchema() { return ` - type ${module.name}List { - list: [${module.name}] + rows: [${module.name}Module] pagination: Pagination sorting: Sorting paginationProperties: PaginationProperties @deprecated(reason: "Use pagination instead") } type ${module.name}BulkMutationResponse { - returning: [${module.name}] + returning: [${module.name}Module] affectedRows: Int } type Count${module.name} { @@ -29,8 +32,8 @@ export default function (module, schemaInformation, store) { } extend type Query { - view${module.name}(where: ${module.name}FilterInput): ${module.name} - list${module.name}(pagination: PaginationInput, where: ${module.name}FilterInput, sorting: [SortingInput]): ${module.name}List + ${singleRowFieldName}(where: ${module.name}FilterInput): ${module.name}Module + ${rowsFieldName}(pagination: PaginationInput, where: ${module.name}FilterInput, sorting: [SortingInput]): ${module.name}List count${module.name}(where: ${module.name}FilterInput): Int }` }, @@ -40,7 +43,7 @@ export default function (module, schemaInformation, store) { update${module.name}(input: update${module.name}Input,where: ${module.name}FilterInput!): ${module.name}BulkMutationResponse insert${module.name}(input: [insert${module.name}Input]): ${module.name}BulkMutationResponse delete${module.name}(where: ${module.name}FilterInput!): SuccessResponse - InsertOrUpdate${module.name}(id: Int, input: insert${module.name}Input): ${module.name} + InsertOrUpdate${module.name}(id: Int, input: insert${module.name}Input): ${module.name}List } ` }, @@ -171,7 +174,7 @@ export default function (module, schemaInformation, store) { ), }, Query: { - [`view${module.name}`]: get( + [singleRowFieldName]: get( module, "graphql.queries.view", async (_, args, context, info) => { @@ -208,13 +211,13 @@ export default function (module, schemaInformation, store) { return find } ), - [`list${module.name}`]: get( + [rowsFieldName]: get( module, "graphql.queries.list", async (_, args, context, info) => { wLogWithDateWithInfo( "[Wertik-GraphQL-Query]", - `list${module.name} - args ${JSON.stringify(args)}` + `${rowsFieldName} - args ${JSON.stringify(args)}` ) const argsFromEvent = await get( module, @@ -232,7 +235,7 @@ export default function (module, schemaInformation, store) { ), { attributes: generateRequestedFieldsFromGraphqlInfo( - graphqlFields(info).list + graphqlFields(info).rows ), } ) diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 3407d43e..29cf5376 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -8,7 +8,8 @@ export const paginate = async ( includes: any[] = [], queryOptions: { [key: string]: any } = {} ) => { - const { page = 1, limit = 100, sorting = [] } = arg.pagination ?? {} + const { page = 1, limit = 100 } = arg.pagination ?? {} + const sorting = arg.sorting ?? [] const offset = limit * (page - 1) const keys = [ ...store.database.relationships.map((c) => c.graphqlKey), @@ -36,7 +37,7 @@ export const paginate = async ( limit, } return { - list: rows, + rows: rows, paginationProperties: pagination, pagination, } diff --git a/src/database/database.ts b/src/database/database.ts index c753f6e0..f3f58573 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,6 +1,4 @@ -import get from "lodash.get" import { wLog, wLogWithInfo } from "../utils/log" -import { paginate } from "../crud/paginate" import { Store, WertikApp } from "../types" import logSymbols from "log-symbols" @@ -28,54 +26,3 @@ export const applyRelationshipsFromStoreToDatabase = async ( }) if (store.database.relationships.length > 0) wLog("\n") } - -export const applyRelationshipsFromStoreToGraphql = async ( - store: Store, - _app: WertikApp -) => { - store.database.relationships.forEach((element) => { - const oldResolvers = get( - store, - `graphql.resolvers.${element.currentModule}`, - {} - ) - - store.graphql.resolvers[element.currentModule] = { - ...oldResolvers, - [element.graphqlKey]: async (parent, _args, context, info) => { - const tableInstance = - context.wertik.modules[element.referencedModule].tableInstance - let referencedModuleKey = - element.options.sourceKey || element.options.targetKey - let currentModuleKey = element.options.foreignKey || "id" - - if (!referencedModuleKey) { - referencedModuleKey = "id" - } - - if (["hasOne", "belongsTo"].includes(element.type)) { - if (parent[info.fieldName]) { - return parent[info.fieldName] - } - return await tableInstance.findOne({ - where: { - [currentModuleKey]: parent[referencedModuleKey], - }, - }) - } else if (["hasMany", "belongsToMany"]) { - if (parent[info.fieldName]) { - return { list: parent[info.fieldName] } - } - return await paginate( - { - where: { - [currentModuleKey]: parent[referencedModuleKey], - }, - }, - tableInstance - ) - } - }, - } - }) -} diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 8d3e8f48..71588b2e 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -2,7 +2,7 @@ import { Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" import { UseMysqlDatabaseProps } from "../../types/database" import get from "lodash.get" -import { wLog, wLogWithError, wLogWithSuccess } from "../../utils/log" +import { wLog, wLogWithError, wLogWithInfo, wLogWithSuccess } from "../../utils/log" export const getAllRelationships = (dbName: string) => { return ` @@ -27,6 +27,9 @@ export const useMysqlDatabase = function (obj: UseMysqlDatabaseProps) { }) await sequelize.authenticate().catch((error) => { wLogWithError("[DB] Connecting failed to database", obj.name) + wLogWithError("[DB] Error", error.message) + wLogWithInfo("[DB] Error Info") + wLog(error) process.exit(1) }) wLogWithSuccess( diff --git a/src/devServer.ts b/src/devServer.ts index 7bd83617..072626dc 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -13,19 +13,21 @@ import wertik, { wertik({ port: 1200, - graphql: useGraphql(), + graphql: useGraphql({ + storeTypeDefFilePath: process.cwd() + "/graphqlSchema.graphql", + }), database: { ecommerce: useMysqlDatabase({ port: 3306, name: "wertik", - host: "localhost", + host: "127.0.0.1", password: "pass", username: "root", }), wapgee_prod: useMysqlDatabase({ port: 3306, name: "wapgee_prod", - host: "localhost", + host: "127.0.0.1", password: "pass", username: "root", }), diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 92e41a77..8ca4866a 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -4,6 +4,8 @@ import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" import { UseGraphqlProps, GraphqlInitializeProps } from "../types/graphql" import { wLogWithSuccess } from "../utils/log" +import fs from "fs" +import path from "path" export const useGraphql = (props?: UseGraphqlProps) => { return ({ @@ -29,6 +31,12 @@ export const useGraphql = (props?: UseGraphqlProps) => { const options = { ...get(configuration, "graphql.options", {}) } + if (props && props.storeTypeDefFilePath) { + if (fs.existsSync(props.storeTypeDefFilePath)) + fs.unlinkSync(props.storeTypeDefFilePath) + fs.writeFileSync(props.storeTypeDefFilePath, store.graphql.typeDefs) + } + const GraphqlApolloServer = new ApolloServer({ typeDefs: store.graphql.typeDefs, resolvers: { diff --git a/src/index.ts b/src/index.ts index 8637d74f..f338270f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,6 @@ import express from "express" import store, { wertikApp } from "./store" import { applyRelationshipsFromStoreToDatabase, - applyRelationshipsFromStoreToGraphql, } from "./database/database" import { emailSender } from "./mailer/index" import http from "http" @@ -132,7 +131,6 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( } applyRelationshipsFromStoreToDatabase(store, wertikApp) - applyRelationshipsFromStoreToGraphql(store, wertikApp) expressApp.get("/w/info", function (req, res) { res.json({ diff --git a/src/modules/modules.ts b/src/modules/modules.ts index c8c2c838..8c36d178 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -6,6 +6,8 @@ import { getUpdateSchema, generateEnumTypeForGraphql, generateGenerateGraphQLCrud, + generateRowsFieldNameForModuleName, + generateRowFieldNameForModuleName, } from "./modulesHelpers" import { getMysqlTableInfo } from "../database/mysql/getTableInfo" import { Store, WertikApp, WertikConfiguration } from "./../types/index" @@ -29,7 +31,7 @@ export const useModule = (moduleProps: UseModuleProps) => { }) => { let currentModuleRelationships = [] let tableInstance: ModelStatic> - let graphqlSchema = [`type ${moduleProps.name} {`] + let graphqlSchema = [`type ${moduleProps.name}Module {`] let listSchema = "" let filterSchema = [`input ${moduleProps.name}FilterInput {`] @@ -74,7 +76,7 @@ export const useModule = (moduleProps: UseModuleProps) => { const hasOne = (params: RelationParams) => { graphqlSchema.push( - `${params.graphqlKey}: ${params.module}` + `${params.graphqlKey}: ${params.module}Module` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -94,7 +96,7 @@ export const useModule = (moduleProps: UseModuleProps) => { } const belongsTo = (params: RelationParams) => { graphqlSchema.push( - `${params.graphqlKey}: ${params.module}` + `${params.graphqlKey}: ${params.module}Module` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -229,7 +231,7 @@ export const useModule = (moduleProps: UseModuleProps) => { listSchema = ` query List${moduleProps.name} { - list: [${moduleProps.name}] + rows: [${moduleProps.name}] paginationProperties: PaginationProperties filters: ${moduleProps.name}Filters } diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index a7535532..612ddeba 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -4,6 +4,8 @@ import { TableInfo } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" import crud from "../crud" import store from "../store" +import pluralize from "pluralize" +import snackCase from "lodash.snakecase" export const generateDataTypeFromDescribeTableColumnType = (Type: string) => { let length = Type.match(/[0-9]/g)?.join("") @@ -160,5 +162,14 @@ export const generateRequestedFieldsFromGraphqlInfo = (info) => { "__typename", "__arguments", ] + return Object.keys(info).filter((c) => !keys.includes(c)) } + + +export const generateRowFieldNameForModuleName = (moduleName) => { + return snackCase(pluralize.singular(moduleName)).toLowerCase() +} +export const generateRowsFieldNameForModuleName = (moduleName) => { + return snackCase(pluralize.plural(moduleName)).toLowerCase() +} \ No newline at end of file diff --git a/src/types/graphql.ts b/src/types/graphql.ts index 68e9c850..7f53f3c9 100644 --- a/src/types/graphql.ts +++ b/src/types/graphql.ts @@ -15,6 +15,7 @@ export interface UseGraphqlProps { Query: {} } typeDefs?: string + storeTypeDefFilePath?: string; } export interface GraphqlInitializeProps { From 8c3fe495b7796ffaea8f522f3cbe3dc1358a4bda Mon Sep 17 00:00:00 2001 From: ilyas Date: Thu, 28 Sep 2023 06:01:36 +0500 Subject: [PATCH 35/52] Converted graphql types to camel case --- changelog.md | 11 +++++++---- docs/v3/modules.md | 22 +++++++++++----------- src/crud/index.ts | 29 ++++++++++++++++------------- src/database/mysql/mysql.ts | 7 ++++++- src/graphql/generalSchema.ts | 18 +++++++++--------- src/index.ts | 4 +--- src/modules/modules.ts | 28 ++++++++++++---------------- src/modules/modulesHelpers.ts | 6 +++--- src/types/graphql.ts | 2 +- tests/database_and_graphql.test.js | 5 +++-- 10 files changed, 69 insertions(+), 63 deletions(-) diff --git a/changelog.md b/changelog.md index 13ad7674..e6fa0142 100644 --- a/changelog.md +++ b/changelog.md @@ -8,15 +8,18 @@ - Added recursion support for both list and view queries. - Added support for filtering associated data in recursive queries. - Removed moment.js and replaced with dayjs. -- Scanned and verified fixes with SonarQube. +- Scanned and verified fixes with SonarQube. - Removed lodash, installed its child packages to reduce app size. - Added `startServer()`, `restartServer()`, `stopServer()` methods -- Added tests to ensure graphql operations work perfectly. -- Changed graphql list field `list` to `rows`. -- Added `Module` after module name in graphql types. For example, `User` is now `UserModule`. + +#### Graphql + - Changed list query to `users` instead of `listUsers`, `EcommerceUsers` becomes `ecommerce_users`. - Changed view query to singular `user` instead of `viewUser`, `EcommerceUser` becomes `ecommerce_user`. +- Changed graphql list field `list` to `rows`. - Removed method `applyRelationshipsFromStoreToGraphql`, since we are moving to eager loading. +- Added `Module` after module name in graphql types. For example, `User` is now `UserModule`. +- Added tests to ensure graphql operations work perfectly. - Added option to output graphql type definitions in a file. ### 3.3.0 diff --git a/docs/v3/modules.md b/docs/v3/modules.md index 9b0835e9..39b193c8 100644 --- a/docs/v3/modules.md +++ b/docs/v3/modules.md @@ -98,13 +98,13 @@ input updateGamesInput { For filtering data from `games` table, Wertik JS will also create an input for filtering: ```graphql -input GamesFilterInput { - name: StringFilterInput - publisher: StringFilterInput +input Gamesfilter_input { + name: Stringfilter_input + publisher: Stringfilter_input } ``` -To explore more about `StringFilterInput` and other filter input please visit GraphQL Playground to get more familiar with it. +To explore more about `Stringfilter_input` and other filter input please visit GraphQL Playground to get more familiar with it. ## This will generate @@ -117,13 +117,13 @@ To explore more about `StringFilterInput` and other filter input please visit Gr ```graphql type Query { version: String - viewGames(where: GamesFilterInput): Games + viewGames(where: Gamesfilter_input): Games listGames( pagination: PaginationInput - where: GamesFilterInput + where: Gamesfilter_input sorting: [SortingInput] ): GamesList - countGames(where: GamesFilterInput): Int + countGames(where: Gamesfilter_input): Int } ``` @@ -140,10 +140,10 @@ type Mutation { version: String updateGames( input: updateGamesInput - where: GamesFilterInput! + where: Gamesfilter_input! ): GamesBulkMutationResponse createGames(input: [createGamesInput]): GamesBulkMutationResponse - deleteGames(where: GamesFilterInput!): SuccessResponse + deleteGames(where: Gamesfilter_input!): SuccessResponse createOrUpdateGames(id: Int, input: createGamesInput): Games } ``` @@ -157,7 +157,7 @@ When you provide `useMysqlDatabase: true` for a module called Games. Wertik JS w ```graphql listGames( pagination: PaginationInput - where: GamesFilterInput + where: Gamesfilter_input sorting: [SortingInput] ): GamesList ``` @@ -171,7 +171,7 @@ input PaginationInput { } ``` -And `GamesFilterInput` is same as Sequelize search object but main keywords such as like, `eq` or `like` starts with `_`, For example: +And `Gamesfilter_input` is same as Sequelize search object but main keywords such as like, `eq` or `like` starts with `_`, For example: ```graphql query GamesList { diff --git a/src/crud/index.ts b/src/crud/index.ts index e33405aa..6841af28 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -1,7 +1,11 @@ import get from "lodash.get" import { wLogWithDateWithInfo } from "../utils/log" import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" -import { generateRequestedFieldsFromGraphqlInfo, generateRowFieldNameForModuleName, generateRowsFieldNameForModuleName } from "../modules/modulesHelpers" +import { + generateRequestedFieldsFromGraphqlInfo, + generateRowFieldNameForModuleName, + generateRowsFieldNameForModuleName, +} from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import graphqlFields from "graphql-fields" import { paginate } from "./paginate" @@ -9,7 +13,6 @@ import omit from "lodash.omit" import { voidFunction } from "../utils/voidFunction" export default function (module, schemaInformation, store) { - let rowsFieldName = generateRowsFieldNameForModuleName(module.name) let singleRowFieldName = generateRowFieldNameForModuleName(module.name) @@ -32,25 +35,25 @@ export default function (module, schemaInformation, store) { } extend type Query { - ${singleRowFieldName}(where: ${module.name}FilterInput): ${module.name}Module - ${rowsFieldName}(pagination: PaginationInput, where: ${module.name}FilterInput, sorting: [SortingInput]): ${module.name}List - count${module.name}(where: ${module.name}FilterInput): Int + ${singleRowFieldName}(where: ${singleRowFieldName}_filter_input): ${module.name}Module + ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, sorting: [SortingInput]): ${module.name}List + count${module.name}(where: ${singleRowFieldName}_filter_input): Int }` }, generateMutationsCrudSchema() { return ` extend type Mutation { - update${module.name}(input: update${module.name}Input,where: ${module.name}FilterInput!): ${module.name}BulkMutationResponse - insert${module.name}(input: [insert${module.name}Input]): ${module.name}BulkMutationResponse - delete${module.name}(where: ${module.name}FilterInput!): SuccessResponse - InsertOrUpdate${module.name}(id: Int, input: insert${module.name}Input): ${module.name}List + update_${rowsFieldName}(input: update${module.name}Input,where: ${singleRowFieldName}_filter_input!): ${module.name}BulkMutationResponse + insert_${rowsFieldName}(input: [insert_${rowsFieldName}_input]): ${module.name}BulkMutationResponse + delete_${rowsFieldName}(where: ${singleRowFieldName}_filter_input!): SuccessResponse + insert_or_update_${rowsFieldName}(id: Int, input: insert_${rowsFieldName}_input): ${module.name}List } ` }, generateCrudResolvers() { return { Mutation: { - [`InsertOrUpdate${module.name}`]: get( + [`insert_or_update_${rowsFieldName}`]: get( module, "graphql.mutations.InsertOrUpdate", async (_, args, context, info) => { @@ -91,7 +94,7 @@ export default function (module, schemaInformation, store) { } } ), - [`update${module.name}`]: get( + [`update_${rowsFieldName}`]: get( module, "graphql.mutations.update", async (_, args, context, info) => { @@ -123,7 +126,7 @@ export default function (module, schemaInformation, store) { } } ), - [`delete${module.name}`]: get( + [`delete_${rowsFieldName}`]: get( module, "graphql.mutations.delete", async (_, args, context, info) => { @@ -146,7 +149,7 @@ export default function (module, schemaInformation, store) { return { message: `${module.name} Deleted` } } ), - [`insert${module.name}`]: get( + [`insert_${rowsFieldName}`]: get( module, "graphql.mutations.create", async (_, args, context, info) => { diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 71588b2e..60c632d5 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -2,7 +2,12 @@ import { Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" import { UseMysqlDatabaseProps } from "../../types/database" import get from "lodash.get" -import { wLog, wLogWithError, wLogWithInfo, wLogWithSuccess } from "../../utils/log" +import { + wLog, + wLogWithError, + wLogWithInfo, + wLogWithSuccess, +} from "../../utils/log" export const getAllRelationships = (dbName: string) => { return ` diff --git a/src/graphql/generalSchema.ts b/src/graphql/generalSchema.ts index cd5a1664..0df407b0 100644 --- a/src/graphql/generalSchema.ts +++ b/src/graphql/generalSchema.ts @@ -6,7 +6,7 @@ export default ` scalar JSON scalar JSONObject - input StringFilterInput { + input string_filter_input { _eq: String _ne: String _like: String @@ -20,12 +20,12 @@ export default ` _notRegexp: String _iRegexp: String _notIRegexp: String - _or: StringFilterInput - _and: StringFilterInput + _or: string_filter_input + _and: string_filter_input _between: [String] _notBetween: [String] } - input IntFilterInput { + input int_filter_input { _eq: Int _gt: Int _gte: Int @@ -36,10 +36,10 @@ export default ` _notBetween: [Int] _in: [Int] _notIn: [Int] - _or: IntFilterInput - _and: IntFilterInput + _or: int_filter_input + _and: int_filter_input } - input DateFilterInput { + input date_filter_input { _eq: String _gt: String _gte: String @@ -49,7 +49,7 @@ export default ` _neq: String _nin: [String!] } - input BooleanFilterInput { + input boolean_filter_input { _eq: Boolean _ne: Boolean } @@ -65,7 +65,7 @@ export default ` page: Int limit: Int } - input FilterInput { + input filter_input { column: String! operator: String! value: String! diff --git a/src/index.ts b/src/index.ts index f338270f..8036e806 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,7 @@ import get from "lodash.get" import express from "express" import store, { wertikApp } from "./store" -import { - applyRelationshipsFromStoreToDatabase, -} from "./database/database" +import { applyRelationshipsFromStoreToDatabase } from "./database/database" import { emailSender } from "./mailer/index" import http from "http" import { WertikConfiguration, WertikApp } from "./types" diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 8c36d178..d53120ec 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -33,7 +33,7 @@ export const useModule = (moduleProps: UseModuleProps) => { let tableInstance: ModelStatic> let graphqlSchema = [`type ${moduleProps.name}Module {`] let listSchema = "" - let filterSchema = [`input ${moduleProps.name}FilterInput {`] + let filterSchema = [`input ${generateRowFieldNameForModuleName(moduleProps.name)}_filter_input {`] const useDatabase = get(moduleProps, "useDatabase", false) @@ -75,9 +75,7 @@ export const useModule = (moduleProps: UseModuleProps) => { } const hasOne = (params: RelationParams) => { - graphqlSchema.push( - `${params.graphqlKey}: ${params.module}Module` - ) + graphqlSchema.push(`${params.graphqlKey}: ${params.module}Module`) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -91,13 +89,11 @@ export const useModule = (moduleProps: UseModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${params.module}FilterInput` + `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName(params.module)}_filter_input` ) } const belongsTo = (params: RelationParams) => { - graphqlSchema.push( - `${params.graphqlKey}: ${params.module}Module` - ) + graphqlSchema.push(`${params.graphqlKey}: ${params.module}Module`) let relationshipInfo = { currentModule: moduleProps.name, currentModuleDatabase: moduleProps.database, @@ -111,12 +107,12 @@ export const useModule = (moduleProps: UseModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${params.module}FilterInput` + `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName(params.module)}_filter_input` ) } const belongsToMany = (params: RelationParams) => { graphqlSchema.push( - `${params.graphqlKey}(offset: Int, limit: Int, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List` + `${params.graphqlKey}(offset: Int, limit: Int, where: ${generateRowFieldNameForModuleName(params.module)}_filter_input, sorting: [SortingInput]): ${params.module}List` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -131,12 +127,12 @@ export const useModule = (moduleProps: UseModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${params.module}FilterInput` + `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName(params.module)}_filter_input` ) } const hasMany = (params: RelationParams) => { graphqlSchema.push( - `${params.graphqlKey}(offset: Int, limit: Int, where: ${params.module}FilterInput, sorting: [SortingInput]): ${params.module}List` + `${params.graphqlKey}(offset: Int, limit: Int, where: ${generateRowFieldNameForModuleName(params.module)}_filter_input, sorting: [SortingInput]): ${params.module}List` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -151,7 +147,7 @@ export const useModule = (moduleProps: UseModuleProps) => { store.database.relationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${params.module}FilterInput` + `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName(params.module)}_filter_input` ) } get(moduleProps, "on", () => {})({ @@ -221,12 +217,12 @@ export const useModule = (moduleProps: UseModuleProps) => { insertSchema = getInsertSchema(moduleProps, tableInfo) tableInfo.columns.forEach((column) => { - let filterInput = + let filter_input = column.databaseType.toLowerCase() === "enum" ? `${column.columnName}: ${column.graphqlType}` - : `${column.columnName}: ${column.graphqlType}FilterInput` + : `${column.columnName}: ${column.graphqlType.toLowerCase()}_filter_input` - filterSchema.push(filterInput) + filterSchema.push(filter_input) }) listSchema = ` diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 612ddeba..776a6b81 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -86,8 +86,9 @@ export const getInsertSchema = ( tableInfo: TableInfo ) => { const optionsInsertSchema = get(module, "graphql.createSchema", "") + const rowsFieldName = generateRowsFieldNameForModuleName(module.name) if (optionsInsertSchema) return optionsInsertSchema - let insertSchema = [`input insert${module.name}Input {`] + let insertSchema = [`input insert_${rowsFieldName}_input {`] tableInfo.columns.forEach((column) => { if (column.columnName !== "id" && !column.isDateColumn) { insertSchema.push( @@ -166,10 +167,9 @@ export const generateRequestedFieldsFromGraphqlInfo = (info) => { return Object.keys(info).filter((c) => !keys.includes(c)) } - export const generateRowFieldNameForModuleName = (moduleName) => { return snackCase(pluralize.singular(moduleName)).toLowerCase() } export const generateRowsFieldNameForModuleName = (moduleName) => { return snackCase(pluralize.plural(moduleName)).toLowerCase() -} \ No newline at end of file +} diff --git a/src/types/graphql.ts b/src/types/graphql.ts index 7f53f3c9..b5e5d822 100644 --- a/src/types/graphql.ts +++ b/src/types/graphql.ts @@ -15,7 +15,7 @@ export interface UseGraphqlProps { Query: {} } typeDefs?: string - storeTypeDefFilePath?: string; + storeTypeDefFilePath?: string } export interface GraphqlInitializeProps { diff --git a/tests/database_and_graphql.test.js b/tests/database_and_graphql.test.js index 302a8807..d570d1ba 100644 --- a/tests/database_and_graphql.test.js +++ b/tests/database_and_graphql.test.js @@ -98,7 +98,7 @@ if (database.name) { let viewItem = await app.graphql.executeOperation({ query: ` query { - viewShirts(where: { sizes: xxxl }) { + shirt(where: { sizes: xxxl }) { id sizes } @@ -106,7 +106,8 @@ if (database.name) { `, }) - expect(viewItem.data.viewShirts.sizes).toBe("xxxl") + console.log(viewItem) + expect(viewItem.data.shirt.sizes).toBe("xxxl") }) // deleted test("Expect graphql to delete data", async () => { From dc0603049ee5f6918e483140581fbf766ed3b44e Mon Sep 17 00:00:00 2001 From: ilyas Date: Thu, 28 Sep 2023 06:02:34 +0500 Subject: [PATCH 36/52] Updated change log --- changelog.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/changelog.md b/changelog.md index e6fa0142..b703beb5 100644 --- a/changelog.md +++ b/changelog.md @@ -11,9 +11,6 @@ - Scanned and verified fixes with SonarQube. - Removed lodash, installed its child packages to reduce app size. - Added `startServer()`, `restartServer()`, `stopServer()` methods - -#### Graphql - - Changed list query to `users` instead of `listUsers`, `EcommerceUsers` becomes `ecommerce_users`. - Changed view query to singular `user` instead of `viewUser`, `EcommerceUser` becomes `ecommerce_user`. - Changed graphql list field `list` to `rows`. From 94b4d1cdbe2af25be8dbef412809f5164997a0de Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Fri, 10 Nov 2023 06:12:56 +0500 Subject: [PATCH 37/52] v3.4 added more test and replaced rows with list in function convertGraphqlRequestedFieldsIntoInclude --- changelog.md | 2 +- src/database/eagerLoadingGraphqlQuery.ts | 6 +- src/devServer.ts | 128 +++++++++++++------- src/modules/modules.ts | 38 ++++-- test_database.sql | 39 ++++-- tests/database.test.js | 22 ++++ tests/database_and_graphql.test.js | 126 -------------------- tests/graphql.test.js | 145 +++++++++++++++++++++++ tests/testUtils.js | 45 +++++++ 9 files changed, 357 insertions(+), 194 deletions(-) create mode 100644 tests/database.test.js delete mode 100644 tests/database_and_graphql.test.js create mode 100644 tests/graphql.test.js create mode 100644 tests/testUtils.js diff --git a/changelog.md b/changelog.md index b703beb5..93b385df 100644 --- a/changelog.md +++ b/changelog.md @@ -13,7 +13,7 @@ - Added `startServer()`, `restartServer()`, `stopServer()` methods - Changed list query to `users` instead of `listUsers`, `EcommerceUsers` becomes `ecommerce_users`. - Changed view query to singular `user` instead of `viewUser`, `EcommerceUser` becomes `ecommerce_user`. -- Changed graphql list field `list` to `rows`. +- Changed graphql list field `list` to `rows`. - Removed method `applyRelationshipsFromStoreToGraphql`, since we are moving to eager loading. - Added `Module` after module name in graphql types. For example, `User` is now `UserModule`. - Added tests to ensure graphql operations work perfectly. diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 0b2c597d..6805a4d9 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -8,9 +8,9 @@ import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelper const clean = (cleanObject) => { let recursion = (_obj) => { Object.keys(_obj).forEach((key) => { - if (key === "list") { - _obj = { ..._obj, ..._obj["list"] } - delete _obj["list"] + if (key === "rows") { + _obj = { ..._obj, ..._obj["rows"] } + delete _obj["rows"] } }) diff --git a/src/devServer.ts b/src/devServer.ts index 072626dc..c468fa42 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -33,16 +33,16 @@ wertik({ }), }, modules: { - EcommerceShirts: useModule({ - name: "EcommerceShirts", + Product: useModule({ + name: "Product", useDatabase: true, database: "ecommerce", - table: "shirts", + table: "product", on: function ({ belongsTo }) { belongsTo({ database: "ecommerce", graphqlKey: "user", - module: "EcommerceUsers", + module: "User", options: { as: "user", foreignKey: "user_id", @@ -51,60 +51,96 @@ wertik({ }) }, }), - EcommerceUsers: useModule({ - name: "EcommerceUsers", + User: useModule({ + name: "User", useDatabase: true, database: "ecommerce", - table: "users", + table: "user", on: function ({ hasMany }) { hasMany({ database: "ecommerce", - graphqlKey: "shirts", - module: "EcommerceShirts", + graphqlKey: "products", + module: "Product", options: { - as: "shirts", + as: "products", foreignKey: "user_id", sourceKey: "id", }, }) }, }), - User: useModule({ - name: "User", - useDatabase: true, - table: "users", - database: "wapgee_prod", - on: function ({ hasMany }) { - hasMany({ - database: "wapgee_prod", - graphqlKey: "posts", - module: "Post", - options: { - as: "posts", - foreignKey: "created_by", - sourceKey: "id", - }, - }) - }, - }), - Post: useModule({ - name: "Post", - useDatabase: true, - table: "post", - database: "wapgee_prod", - on: function ({ hasOne }) { - hasOne({ - module: "User", - graphqlKey: "author", - database: "default", - options: { - as: "author", - sourceKey: "created_by", - foreignKey: "id", - }, - }) - }, - }), + // EcommerceShirts: useModule({ + // name: "EcommerceShirts", + // useDatabase: true, + // database: "ecommerce", + // table: "shirts", + // on: function ({ belongsTo }) { + // belongsTo({ + // database: "ecommerce", + // graphqlKey: "user", + // module: "EcommerceUsers", + // options: { + // as: "user", + // foreignKey: "user_id", + // targetKey: "id", + // }, + // }) + // }, + // }), + // EcommerceUsers: useModule({ + // name: "EcommerceUsers", + // useDatabase: true, + // database: "ecommerce", + // table: "users", + // on: function ({ hasMany }) { + // hasMany({ + // database: "ecommerce", + // graphqlKey: "shirts", + // module: "EcommerceShirts", + // options: { + // as: "shirts", + // foreignKey: "user_id", + // sourceKey: "id", + // }, + // }) + // }, + // }), + // User: useModule({ + // name: "User", + // useDatabase: true, + // table: "users", + // database: "wapgee_prod", + // on: function ({ hasMany }) { + // hasMany({ + // database: "wapgee_prod", + // graphqlKey: "posts", + // module: "Post", + // options: { + // as: "posts", + // foreignKey: "created_by", + // sourceKey: "id", + // }, + // }) + // }, + // }), + // Post: useModule({ + // name: "Post", + // useDatabase: true, + // table: "post", + // database: "wapgee_prod", + // on: function ({ hasOne }) { + // hasOne({ + // module: "User", + // graphqlKey: "author", + // database: "default", + // options: { + // as: "author", + // sourceKey: "created_by", + // foreignKey: "id", + // }, + // }) + // }, + // }), }, sockets: { mySockets: useWebSockets({ diff --git a/src/modules/modules.ts b/src/modules/modules.ts index d53120ec..ce4ea246 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -33,7 +33,11 @@ export const useModule = (moduleProps: UseModuleProps) => { let tableInstance: ModelStatic> let graphqlSchema = [`type ${moduleProps.name}Module {`] let listSchema = "" - let filterSchema = [`input ${generateRowFieldNameForModuleName(moduleProps.name)}_filter_input {`] + let filterSchema = [ + `input ${generateRowFieldNameForModuleName( + moduleProps.name + )}_filter_input {`, + ] const useDatabase = get(moduleProps, "useDatabase", false) @@ -89,7 +93,9 @@ export const useModule = (moduleProps: UseModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName(params.module)}_filter_input` + `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName( + params.module + )}_filter_input` ) } const belongsTo = (params: RelationParams) => { @@ -107,12 +113,18 @@ export const useModule = (moduleProps: UseModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName(params.module)}_filter_input` + `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName( + params.module + )}_filter_input` ) } const belongsToMany = (params: RelationParams) => { graphqlSchema.push( - `${params.graphqlKey}(offset: Int, limit: Int, where: ${generateRowFieldNameForModuleName(params.module)}_filter_input, sorting: [SortingInput]): ${params.module}List` + `${ + params.graphqlKey + }(offset: Int, limit: Int, where: ${generateRowFieldNameForModuleName( + params.module + )}_filter_input, sorting: [SortingInput]): [${params.module}Module]` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -127,12 +139,18 @@ export const useModule = (moduleProps: UseModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName(params.module)}_filter_input` + `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName( + params.module + )}_filter_input` ) } const hasMany = (params: RelationParams) => { graphqlSchema.push( - `${params.graphqlKey}(offset: Int, limit: Int, where: ${generateRowFieldNameForModuleName(params.module)}_filter_input, sorting: [SortingInput]): ${params.module}List` + `${ + params.graphqlKey + }(offset: Int, limit: Int, where: ${generateRowFieldNameForModuleName( + params.module + )}_filter_input, sorting: [SortingInput]): [${params.module}Module]` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -147,7 +165,9 @@ export const useModule = (moduleProps: UseModuleProps) => { store.database.relationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName(params.module)}_filter_input` + `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName( + params.module + )}_filter_input` ) } get(moduleProps, "on", () => {})({ @@ -220,7 +240,9 @@ export const useModule = (moduleProps: UseModuleProps) => { let filter_input = column.databaseType.toLowerCase() === "enum" ? `${column.columnName}: ${column.graphqlType}` - : `${column.columnName}: ${column.graphqlType.toLowerCase()}_filter_input` + : `${ + column.columnName + }: ${column.graphqlType.toLowerCase()}_filter_input` filterSchema.push(filter_input) }) diff --git a/test_database.sql b/test_database.sql index 8d060611..890162f1 100644 --- a/test_database.sql +++ b/test_database.sql @@ -3,27 +3,46 @@ CREATE DATABASE IF NOT EXISTS `wertik` /*!40100 DEFAULT CHARACTER SET utf8mb4 CO USE `wertik`; -DROP TABLE IF EXISTS `shirts`; -DROP TABLE IF EXISTS `users`; +DROP TABLE IF EXISTS `product`; +DROP TABLE IF EXISTS `user`; +DROP TABLE IF EXISTS `category`; -CREATE TABLE `users` ( +CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1; -CREATE TABLE `shirts` ( +CREATE TABLE `product` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, `sizes` enum('lg','sm','xl','xxl','xxxl') DEFAULT NULL, `user_id` bigint(20), + `category_id` bigint(20), PRIMARY KEY (`id`), - FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL + FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL, + FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE SET NULL ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; -INSERT INTO `users` (`id`, `name`, `email`) VALUES -(12, '123', 'ads'), -(13, '123', 'ads'); +-- add table category +CREATE TABLE `category` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; + +-- insert two users +INSERT INTO `user` (`id`, `name`, `email`) VALUES +(1, 'John', 'asdfasdf@gmail.com'), +(2, 'ali', 'ali@gmail.com'); + +-- insert two categories +INSERT INTO `category` (`id`, `title`) VALUES +(1, 'T-shirt'), +(2, 'Pants'); -INSERT INTO `shirts` (`id`, `sizes`, `user_id`) VALUES -(1, 'xl', 12); +-- insert two products +INSERT INTO `product` (`id`, `title`, `sizes`, `user_id`, `category_id`) VALUES +(1, 'T-shirt 1', 'lg', 1, 1), +(2, 'T-shirt 2', 'sm', 1, 2); diff --git a/tests/database.test.js b/tests/database.test.js new file mode 100644 index 00000000..4a9c62cc --- /dev/null +++ b/tests/database.test.js @@ -0,0 +1,22 @@ +require("dotenv").config() + +const { + default: wertik, + useModule, + useMysqlDatabase, + useGraphql, +} = require("../lib/index") + +const { database } = require("./testUtils") + +if (database.name) { + test("Expect test database to connect and does not causes error", async () => { + await expect( + wertik({ + database: { + default: useMysqlDatabase(database), + }, + }) + ).resolves.not.toThrowError() + }) +} diff --git a/tests/database_and_graphql.test.js b/tests/database_and_graphql.test.js deleted file mode 100644 index d570d1ba..00000000 --- a/tests/database_and_graphql.test.js +++ /dev/null @@ -1,126 +0,0 @@ -require("dotenv").config() - -const { - default: wertik, - useModule, - useMysqlDatabase, - useGraphql, -} = require("../lib/index") - -const database = { - name: process.env.TEST_DATABASE_NAME, - host: process.env.TEST_DATABASE_HOST, - password: process.env.TEST_DATABASE_PASSWORD, - port: process.env.TEST_DATABASE_PORT, - username: process.env.TEST_DATABASE_USERNAME, -} - -const shirtModule = { - name: "Shirts", - useDatabase: true, - database: "default", - table: process.env.TEST_DATABASE_TABLE, -} - -if (database.name) { - test("Expect test database to connect and does not causes error", async () => { - await expect( - wertik({ - database: { - default: useMysqlDatabase(database), - }, - }) - ).resolves.not.toThrowError() - }) -} - -if (database.name) { - describe("Expect useMysqlDatabase, useModule and useGraphql, and expect module graphql operations work", () => { - let app - test("Expect test database to connect and does not causes error", async () => { - await expect( - (app = wertik({ - database: { - default: useMysqlDatabase(database), - }, - modules: { - shirt: useModule(shirtModule), - }, - graphql: useGraphql(), - }).then((wertikApp) => { - app = wertikApp - })) - ).resolves.not.toThrowError() - }) - - let testItem = null - - test("Expect graphql to insert data", async () => { - // describe create works - testItem = await app.graphql.executeOperation({ - query: ` - mutation { - insertShirts(input: { - sizes: lg - user_id: 12 - }) { - returning { - id - user_id - sizes - } - } - } - `, - }) - expect(testItem.data.insertShirts.returning[0].id).toBeGreaterThan(0) - expect(testItem.data.insertShirts.returning[0].sizes).toBe("lg") - }) - // update - test(`Expect graphql to update data`, async () => { - let updatedItem = await app.graphql.executeOperation({ - query: ` - mutation { - updateShirts(input: { sizes: xxxl}, where: { id: { _eq: ${testItem.data.insertShirts.returning[0].id} } }) { - returning { - id - sizes - } - } - } - `, - }) - expect(updatedItem.data.updateShirts.returning[0].id).toBeGreaterThan(0) - expect(updatedItem.data.updateShirts.returning[0].sizes).toBe("xxxl") - }) - // view - test("Expect graphql to view data", async () => { - let viewItem = await app.graphql.executeOperation({ - query: ` - query { - shirt(where: { sizes: xxxl }) { - id - sizes - } - } - - `, - }) - console.log(viewItem) - expect(viewItem.data.shirt.sizes).toBe("xxxl") - }) - // deleted - test("Expect graphql to delete data", async () => { - let deletedItem = await app.graphql.executeOperation({ - query: ` - mutation { - deleteShirts(where: { id: { _eq: ${testItem.data.insertShirts.returning[0].id} } }) { - message - } - } - `, - }) - expect(deletedItem.data.deleteShirts.message.length).toBeGreaterThan(0) - }) - }) -} diff --git a/tests/graphql.test.js b/tests/graphql.test.js new file mode 100644 index 00000000..7b2ac846 --- /dev/null +++ b/tests/graphql.test.js @@ -0,0 +1,145 @@ +require("dotenv").config() + +const { + default: wertik, + useModule, + useMysqlDatabase, + useGraphql, +} = require("../lib/index") + +const { database, Product, User } = require("./testUtils") + +if (database.name) { + describe("Expect useMysqlDatabase, useModule and useGraphql, and expect module graphql operations work", () => { + let app + test("Expect test database to connect and does not causes error", async () => { + await expect( + (app = wertik({ + database: { + default: useMysqlDatabase(database), + }, + modules: { + Product: useModule(Product), + User: useModule(User), + }, + graphql: useGraphql(), + }).then((wertikApp) => { + app = wertikApp + })) + ).resolves.not.toThrowError() + }) + + let testItem = null + + test("Expect graphql to insert data", async () => { + // describe create works + testItem = await app.graphql.executeOperation({ + query: ` + mutation { + insert_products(input: { + sizes: lg + user_id: 1 + title: "My first product" + category_id: 1 + }) { + returning { + id + user_id + sizes + } + } + } + `, + }) + expect(testItem.data.insert_products.returning[0].id).toBeGreaterThan(0) + expect(testItem.data.insert_products.returning[0].sizes).toBe("lg") + }) + // update + test(`Expect graphql to update data`, async () => { + let updatedItem = await app.graphql.executeOperation({ + query: ` + mutation { + update_products(input: { sizes: xxxl, title: "My product title" }, where: { id: { _eq: ${testItem.data.insert_products.returning[0].id} } }) { + returning { + id + sizes + } + } + } + `, + }) + expect(updatedItem.data.update_products.returning[0].id).toBeGreaterThan( + 0 + ) + expect(updatedItem.data.update_products.returning[0].sizes).toBe("xxxl") + }) + // view + test("Expect graphql to view data", async () => { + let viewItem = await app.graphql.executeOperation({ + query: ` + query { + product(where: { sizes: xxxl }) { + id + sizes + } + } + + `, + }) + expect(viewItem.data.product.sizes).toBe("xxxl") + }) + // deleted + test("Expect graphql to delete data", async () => { + let deletedItem = await app.graphql.executeOperation({ + query: ` + mutation { + delete_products(where: { id: { _eq: ${testItem.data.insert_products.returning[0].id} } }) { + message + } + } + `, + }) + expect(deletedItem.data.delete_products.message.length).toBeGreaterThan(0) + }) + + test("Expect a one to one relationship to work", async () => { + let viewItem = await app.graphql.executeOperation({ + query: ` + query { + product(where: { sizes: xxxl }) { + id + sizes + user { + id + name + } + } + } + + `, + }) + expect(viewItem.data.product.sizes).toBe("xxxl") + expect(viewItem.data.product.user.id).toBeGreaterThan(0) + }) + test("Expect a one to many relationship to work", async () => { + let viewItem = await app.graphql.executeOperation({ + query: ` + query { + user(where: { id: { _eq: 1 } }) { + id + name + products { + id + sizes + } + } + } + + `, + }) + expect(viewItem.data.user.id).toBeGreaterThan(0) + expect(viewItem.data.user.products.length).toBeGreaterThan(0) + expect(viewItem.data.user.products[0].id).toBeGreaterThan(0) + }) + }) +} diff --git a/tests/testUtils.js b/tests/testUtils.js new file mode 100644 index 00000000..9d432aad --- /dev/null +++ b/tests/testUtils.js @@ -0,0 +1,45 @@ +exports.database = { + name: process.env.TEST_DATABASE_NAME, + host: process.env.TEST_DATABASE_HOST, + password: process.env.TEST_DATABASE_PASSWORD, + port: process.env.TEST_DATABASE_PORT, + username: process.env.TEST_DATABASE_USERNAME, +} + +exports.Product = { + name: "Product", + useDatabase: true, + database: "default", + table: process.env.TEST_DATABASE_TABLE_PRODUCT, + on: function ({ belongsTo }) { + belongsTo({ + database: "ecommerce", + graphqlKey: "user", + module: "User", + options: { + as: "user", + foreignKey: "user_id", + targetKey: "id", + }, + }) + }, +} + +exports.User = { + name: "User", + useDatabase: true, + database: "default", + table: process.env.TEST_DATABASE_TABLE_USER, + on: function ({ hasMany }) { + hasMany({ + database: "ecommerce", + graphqlKey: "products", + module: "Product", + options: { + as: "products", + foreignKey: "user_id", + sourceKey: "id", + }, + }) + }, +} From e83c82e53ffcc7048c1d17460bd607e33422d8d4 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Tue, 2 Jan 2024 07:19:14 +0500 Subject: [PATCH 38/52] Update changelog.md --- changelog.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 93b385df..47917576 100644 --- a/changelog.md +++ b/changelog.md @@ -7,17 +7,18 @@ - Added support for loading only requested fields in Sequelize. - Added recursion support for both list and view queries. - Added support for filtering associated data in recursive queries. -- Removed moment.js and replaced with dayjs. +- Replaced moment.js with dayjs. - Scanned and verified fixes with SonarQube. -- Removed lodash, installed its child packages to reduce app size. -- Added `startServer()`, `restartServer()`, `stopServer()` methods -- Changed list query to `users` instead of `listUsers`, `EcommerceUsers` becomes `ecommerce_users`. -- Changed view query to singular `user` instead of `viewUser`, `EcommerceUser` becomes `ecommerce_user`. -- Changed graphql list field `list` to `rows`. -- Removed method `applyRelationshipsFromStoreToGraphql`, since we are moving to eager loading. -- Added `Module` after module name in graphql types. For example, `User` is now `UserModule`. -- Added tests to ensure graphql operations work perfectly. -- Added option to output graphql type definitions in a file. +- Removed lodash and installed its child packages to reduce app size. +- Added methods: `startServer()`, `restartServer()`, `stopServer()`. +- Removed query `list`. For example for Module `Users`, `listUsers` will become `users`, `EcommerceUsers` will become `ecommerce_users`. +- From single query item `view` is removed, For example `viewUser` will become `user` and `EcommerceUser` will become `ecommerce_user` +- Changed GraphQL list field from `list` to `rows`. +- Removed `applyRelationshipsFromStoreToGraphql` method due to the shift towards eager loading. +- Appended `Module` after module names in GraphQL types, e.g., `User` is now `UserModule`. +- Added tests to ensure GraphQL operations function correctly. +- Introduced an option to output GraphQL type definitions in a file. + ### 3.3.0 From 558e6a47c883c8740ac55efddffe73ef7c7e6e43 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Tue, 2 Jan 2024 07:42:00 +0500 Subject: [PATCH 39/52] Finalizing 3.4 --- src/store.ts | 3 +++ src/utils/dayjs.ts | 2 ++ src/utils/log.ts | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/store.ts b/src/store.ts index 62f549df..f029fd6f 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,6 +1,9 @@ import generalSchema from "./graphql/generalSchema" import { WertikApp } from "./types" +/** + * @description This is the store of the app. It contains all the data that is required by the app to run. + */ export const wertikApp: WertikApp = { restartServer: () => {}, stopServer: () => {}, diff --git a/src/utils/dayjs.ts b/src/utils/dayjs.ts index 80d37f92..e73ff4f4 100644 --- a/src/utils/dayjs.ts +++ b/src/utils/dayjs.ts @@ -1,6 +1,8 @@ import dayjs from "dayjs" import localizedFormat from "dayjs/plugin/localizedFormat" +import timezone from "dayjs/plugin/timezone" dayjs.extend(localizedFormat) +dayjs.extend(timezone) export default dayjs diff --git a/src/utils/log.ts b/src/utils/log.ts index 921e01ef..58948b19 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -3,7 +3,8 @@ import chalk from "chalk" export const wLog = console.log export const wLogWithDateWithInfo = (info, ...params) => { - console.log(dayjs().format("L-LT"), chalk.blueBright(info), ...params) + let day = dayjs(); + console.log(day.format("L-LT"), day.format("Z"), chalk.blueBright(info), ...params) } export const wLogWithInfo = (info, ...params) => { From 0bfed49194072008f92d0bee40bbbe2934595d9e Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Wed, 3 Jan 2024 06:22:00 +0500 Subject: [PATCH 40/52] Fixed isNull not working properly --- src/database/mysql/getTableInfo.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/database/mysql/getTableInfo.ts b/src/database/mysql/getTableInfo.ts index 9c60a259..57ded39b 100644 --- a/src/database/mysql/getTableInfo.ts +++ b/src/database/mysql/getTableInfo.ts @@ -45,6 +45,7 @@ export const getMysqlTableInfo = async ( module.name ) let isPrimary = element.Key === "PRI" + const isNull = element.Null === "YES"; return { columnName: element.Field, @@ -53,7 +54,7 @@ export const getMysqlTableInfo = async ( graphqlInsertInputType: graphqlType.graphqlInsertInputType, graphqlUpdateInputType: graphqlType.graphqlUpdateInputType, enumValues: graphqlType.enumValues, - isNull: element.Null === "no" ? false : true, + isNull: isNull, isEnum: graphqlType.isEnum, databaseType: graphqlType.databaseType, isPrimary: isPrimary, From 64e7d5693d1c0b78d726c26a5d555ad72ad2f6b1 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Wed, 3 Jan 2024 06:22:34 +0500 Subject: [PATCH 41/52] refactor: run prettier --- src/database/mysql/getTableInfo.ts | 2 +- src/utils/log.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/database/mysql/getTableInfo.ts b/src/database/mysql/getTableInfo.ts index 57ded39b..2d66ad95 100644 --- a/src/database/mysql/getTableInfo.ts +++ b/src/database/mysql/getTableInfo.ts @@ -45,7 +45,7 @@ export const getMysqlTableInfo = async ( module.name ) let isPrimary = element.Key === "PRI" - const isNull = element.Null === "YES"; + const isNull = element.Null === "YES" return { columnName: element.Field, diff --git a/src/utils/log.ts b/src/utils/log.ts index 58948b19..8aab7a64 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -3,8 +3,13 @@ import chalk from "chalk" export const wLog = console.log export const wLogWithDateWithInfo = (info, ...params) => { - let day = dayjs(); - console.log(day.format("L-LT"), day.format("Z"), chalk.blueBright(info), ...params) + let day = dayjs() + console.log( + day.format("L-LT"), + day.format("Z"), + chalk.blueBright(info), + ...params + ) } export const wLogWithInfo = (info, ...params) => { From d012b23ccddb73b55f41e8ce5d76f0cfecc993e1 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sun, 7 Apr 2024 08:00:07 +0500 Subject: [PATCH 42/52] v3.4 Change configuration prop `skip` to `selfStart` to make it more intuitive. --- changelog.md | 1 + src/index.ts | 8 ++++++-- src/types/index.ts | 9 +++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 47917576..709f2859 100644 --- a/changelog.md +++ b/changelog.md @@ -18,6 +18,7 @@ - Appended `Module` after module names in GraphQL types, e.g., `User` is now `UserModule`. - Added tests to ensure GraphQL operations function correctly. - Introduced an option to output GraphQL type definitions in a file. +- Change configuration prop `skip` to `selfStart` to make it more intuitive. ### 3.3.0 diff --git a/src/index.ts b/src/index.ts index 8036e806..f040ddc5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,7 +31,11 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( configuration.appEnv = configuration.appEnv ?? "local" const port = get(configuration, "port", 1200) - const skip = get(configuration, "skip", false) + const selfStart = get( + configuration, + "selfStart", + get(configuration, "skip", true) + ) const expressApp = get(configuration, "express", express()) const httpServer = http.createServer(expressApp) @@ -185,7 +189,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( if (!new Object(process.env).hasOwnProperty("TEST_MODE")) { setTimeout(async () => { - if (skip === false) { + if (selfStart === true) { startServer() } resolve({ diff --git a/src/types/index.ts b/src/types/index.ts index 26a92b21..baa847e3 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -44,8 +44,17 @@ export interface WertikConfiguration { httpServer?: iObject /** * [Optional] When passed as true, Wertik will not start server. + * + * @deprecated Use `selfStart` instead. + * @default true */ skip?: boolean + + /** + * When passed as true, Wertik will not start server. + * @default true + */ + selfStart?: boolean /** * Database connections */ From 60264c89d35e880de0e6bb7787fa7efe0e1e67b9 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sun, 7 Apr 2024 08:23:21 +0500 Subject: [PATCH 43/52] v3.4 renaming stuff from use to with --- changelog.md | 12 +++++++ docs/v3/graphql.md | 14 ++++---- docs/v3/modules.md | 22 ++++++------- src/database/mysql/getTableInfo.ts | 4 +-- src/database/mysql/mysql.ts | 6 ++-- src/devServer.ts | 52 +++++++++++++++--------------- src/graphql/index.ts | 7 ++-- src/helpers/modules/backup.ts | 4 +-- src/logger/index.ts | 8 +++-- src/mailer/index.ts | 4 +-- src/modules/modules.ts | 4 +-- src/modules/modulesHelpers.ts | 6 ++-- src/queue/index.ts | 4 +-- src/redis/index.ts | 4 +-- src/sockets/index.ts | 6 ++-- src/types/database.ts | 2 +- src/types/graphql.ts | 2 +- src/types/index.ts | 10 +++--- src/types/modules.ts | 2 +- src/types/queue.ts | 2 +- tests/database.test.js | 6 ++-- tests/graphql.test.js | 16 ++++----- tests/index.test.js | 32 +++++++++--------- 23 files changed, 122 insertions(+), 107 deletions(-) diff --git a/changelog.md b/changelog.md index 709f2859..1eec3223 100644 --- a/changelog.md +++ b/changelog.md @@ -19,6 +19,18 @@ - Added tests to ensure GraphQL operations function correctly. - Introduced an option to output GraphQL type definitions in a file. - Change configuration prop `skip` to `selfStart` to make it more intuitive. +- Rename `useMysqlDatabase` to `withMysqlDatabase` with its props. +- Rename `useRedis` to `withRedis` with its props. +- Rename `useGraphql` to `withGraphql` with its props. +- Rename `useModule` to `withModule` with its props. +- Rename `useMailer` to `withMailer` with its props. +- Rename `useSocketIO` to `withSocketIO` with its props. +- Rename `useWebSockets` to `withWebSockets` with its props. +- Rename `useIndependentWebSocketsServer` to `withIndependentWebSocketsServer` with its props. +- Rename `useLogger` to `withLogger` with its props. +- Rename `useWinstonTransport` to `withWinstonTransport` with its props. +- Rename `useQueue` to `withQueue` with its props. +- Verified tests with Jest for renaming changes. ### 3.3.0 diff --git a/docs/v3/graphql.md b/docs/v3/graphql.md index 19ae0980..b70b4dd2 100644 --- a/docs/v3/graphql.md +++ b/docs/v3/graphql.md @@ -1,23 +1,23 @@ # GraphQL -For GraphQL, Wertik JS uses Apollo GraphQL under the hood. We choose Apollo GraphQL because it is well managed and bug-free. To set up Graphql Wertik JS provides a function called `useGraphql` to use Graphql in your app. +For GraphQL, Wertik JS uses Apollo GraphQL under the hood. We choose Apollo GraphQL because it is well managed and bug-free. To set up Graphql Wertik JS provides a function called `withGraphql` to use Graphql in your app. ```javascript -import wertik, { useGraphql } from "wertik-js/lib/"; +import wertik, { withGraphql } from "wertik-js/lib/"; wertik({ port: 1200, - graphql: useGraphql(UseGraphqlProps), + graphql: withGraphql(WithGraphqlProps), }); ``` This will initialize GraphQL on URL: http://localhost:1200/graphql. If you visit this link you will Apollo GraphQL playground. -#### UseGraphqlProps +#### WithGraphqlProps -UseGraphqlProps is an argument which is optional when using `useGraphql` method. +WithGraphqlProps is an argument which is optional when using `withGraphql` method. ```typescript -export interface UseGraphqlProps { +export interface WithGraphqlProps { options?: { [key: string]: any; }; @@ -32,5 +32,5 @@ export interface UseGraphqlProps { - options includes ApolloServer options. - applyMiddlewareOptions includes options while integrating Apollo Server with express server with same port. -- resolvers for defined schema in UseGraphqlProps.typeDefs. +- resolvers for defined schema in WithGraphqlProps.typeDefs. - typeDefs is your graphql schema. diff --git a/docs/v3/modules.md b/docs/v3/modules.md index 39b193c8..b79bb6a0 100644 --- a/docs/v3/modules.md +++ b/docs/v3/modules.md @@ -4,16 +4,16 @@ Wertik-js allows extending your app with more features using the `modules` term. ```js import wertik, { - useMysqlDatabase, - useMailer, - useModule, - useGraphql, + withMysqlDatabase, + withMailer, + withModule, + withGraphql, } from "wertik-js/lib/"; weritk({ port: 1200, database: { - default: useMysqlDatabase({ + default: withMysqlDatabase({ name: "default", password: "pass", host: "localhost", @@ -21,16 +21,16 @@ weritk({ username: "root", }), }, - graphql: useGraphql(), + graphql: withGraphql,(), mailer: { - default: useMailer(), + default: withMailer(), }, modules: { - users: useModule({ + users: withModule({ table: "users", database: "default", name: "users", - useMysqlDatabase: true, + useDatabase: true, }), }, }); @@ -48,10 +48,10 @@ When you provide `useMysqlDatabase: true`, `table` and `database`, Wertik JS aut You have to initialize its module in this way: ```js -import wertik, { useModule, useMysqlDatabase, useGraphql } from "wertik-js/lib/"; +import wertik, { useModule, useMysqlDatabase, withGraphql, } from "wertik-js/lib/"; wertik({ port: 1200, - graphql: useGraphql(), + graphql: withGraphql,(), database: { default: useMysqlDatabase({ name: "dbname", diff --git a/src/database/mysql/getTableInfo.ts b/src/database/mysql/getTableInfo.ts index 2d66ad95..3fc1a10a 100644 --- a/src/database/mysql/getTableInfo.ts +++ b/src/database/mysql/getTableInfo.ts @@ -1,4 +1,4 @@ -import { UseModuleProps } from "src/types/modules" +import { WithModuleProps } from "src/types/modules" import { convertDatabaseTypeIntoGraphqlType } from "../helpers" import { MysqlColumnInfoDescribeTable, TableInfo } from "./../../types/database" @@ -30,7 +30,7 @@ export const enumTypes = ["enum"] export const jsonTypes = ["json"] export const getMysqlTableInfo = async ( - module: UseModuleProps, + module: WithModuleProps, sequelize: any ): Promise => { let rows = await sequelize.query(`describe ${module.table};`) diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 60c632d5..12dae3a1 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -1,6 +1,6 @@ import { Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" -import { UseMysqlDatabaseProps } from "../../types/database" +import { WithMysqlDatabaseProps } from "../../types/database" import get from "lodash.get" import { wLog, @@ -20,7 +20,7 @@ export const getAllRelationships = (dbName: string) => { ` } -export const useMysqlDatabase = function (obj: UseMysqlDatabaseProps) { +export const withMysqlDatabase = function (obj: WithMysqlDatabaseProps) { return async () => { try { let sequelize = new Sequelize(obj.name, obj.username, obj.password, { @@ -58,4 +58,4 @@ export const useMysqlDatabase = function (obj: UseMysqlDatabaseProps) { /** * @deprecated use useMysqlDatabase, useDatabase is deprecated and will be removed in 3.5.0 version. */ -export const useDatabase = useMysqlDatabase +export const useDatabase = withMysqlDatabase diff --git a/src/devServer.ts b/src/devServer.ts index c468fa42..75f31b86 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -1,30 +1,30 @@ import wertik, { - useMysqlDatabase, - useGraphql, - useModule, - useWebSockets, - useSocketIO, - useIndependentWebSocketsServer, - useLogger, - useWinstonTransport, - useMailer, - useRedis, + withMysqlDatabase, + withApolloGraphql, + withModule, + withWebSockets, + withSocketIO, + withIndependentWebSocketsServer, + withLogger, + withWinstonTransport, + withMailer, + withRedis, } from "./index" wertik({ port: 1200, - graphql: useGraphql({ + graphql: withApolloGraphql({ storeTypeDefFilePath: process.cwd() + "/graphqlSchema.graphql", }), database: { - ecommerce: useMysqlDatabase({ + ecommerce: withMysqlDatabase({ port: 3306, name: "wertik", host: "127.0.0.1", password: "pass", username: "root", }), - wapgee_prod: useMysqlDatabase({ + wapgee_prod: withMysqlDatabase({ port: 3306, name: "wapgee_prod", host: "127.0.0.1", @@ -33,7 +33,7 @@ wertik({ }), }, modules: { - Product: useModule({ + Product: withModule({ name: "Product", useDatabase: true, database: "ecommerce", @@ -51,7 +51,7 @@ wertik({ }) }, }), - User: useModule({ + User: withModule({ name: "User", useDatabase: true, database: "ecommerce", @@ -69,7 +69,7 @@ wertik({ }) }, }), - // EcommerceShirts: useModule({ + // EcommerceShirts: withModule({ // name: "EcommerceShirts", // useDatabase: true, // database: "ecommerce", @@ -87,7 +87,7 @@ wertik({ // }) // }, // }), - // EcommerceUsers: useModule({ + // EcommerceUsers: withModule({ // name: "EcommerceUsers", // useDatabase: true, // database: "ecommerce", @@ -105,7 +105,7 @@ wertik({ // }) // }, // }), - // User: useModule({ + // User: withModule({ // name: "User", // useDatabase: true, // table: "users", @@ -123,7 +123,7 @@ wertik({ // }) // }, // }), - // Post: useModule({ + // Post: withModule({ // name: "Post", // useDatabase: true, // table: "post", @@ -143,18 +143,18 @@ wertik({ // }), }, sockets: { - mySockets: useWebSockets({ + mySockets: withWebSockets({ path: "/websockets", }), - socketio: useSocketIO({ + socketio: withSocketIO({ path: "/mysocketioserver", }), - mySockets2: useIndependentWebSocketsServer({ + mySockets2: withIndependentWebSocketsServer({ port: 1500, }), }, - logger: useLogger({ - transports: useWinstonTransport((winston) => { + logger: withLogger({ + transports: withWinstonTransport((winston) => { return [ new winston.transports.File({ filename: "info.log", @@ -165,13 +165,13 @@ wertik({ }), mailer: { instances: { - default: useMailer({ + default: withMailer({ name: "Default", }), }, }, redis: { - testRedis: useRedis({ + testRedis: withRedis({ name: "testRedis", }), }, diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 8ca4866a..459510d8 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -2,12 +2,15 @@ import get from "lodash.get" import omit from "lodash.omit" import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" -import { UseGraphqlProps, GraphqlInitializeProps } from "../types/graphql" +import { + WithApolloGraphqlProps, + GraphqlInitializeProps, +} from "../types/graphql" import { wLogWithSuccess } from "../utils/log" import fs from "fs" import path from "path" -export const useGraphql = (props?: UseGraphqlProps) => { +export const withApolloGraphql = (props?: WithApolloGraphqlProps) => { return ({ wertikApp, expressApp, diff --git a/src/helpers/modules/backup.ts b/src/helpers/modules/backup.ts index 93fc2733..0a16617b 100644 --- a/src/helpers/modules/backup.ts +++ b/src/helpers/modules/backup.ts @@ -1,5 +1,5 @@ import dayjs from "./../../utils/dayjs" -import { useModule } from "../../modules/modules" +import { withModule } from "../../modules/modules" import mysqldump from "mysqldump" import fs from "fs" import { wLog } from "../../utils/log" @@ -73,7 +73,7 @@ export const WertikBackupModule = ( table: string, tableOptions: any = {} ) => - useModule({ + withModule({ name: "Backup", useDatabase: true, database: database, diff --git a/src/logger/index.ts b/src/logger/index.ts index 0cb65f51..ba96f88a 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -6,15 +6,17 @@ import { wLogWithSuccess } from "../utils/log" * @param props see interface LoggerOptions from winston * @returns winston instance */ -export const useLogger = (options?: LoggerOptions) => { +export const withLogger = (options?: LoggerOptions) => { wLogWithSuccess(`[Wertik-WinstonLogger]`, `Initialized winston logger`) return winston.createLogger(options) } /** * Allows creating multiple logger instances - * @param fn callback function, useWinstonTransport expects a function and useWinstonTransport runs that function with winston passed so you can return transport instances + * @param fn callback function, withWinstonTransport expects a function and withWinstonTransport runs that function with winston passed so you can return transport instances * @returns should return array of winston transport object. */ -export const useWinstonTransport = (fn = (winstonInstance = winston) => []) => { +export const withWinstonTransport = ( + fn = (winstonInstance = winston) => [] +) => { return fn(winston) } diff --git a/src/mailer/index.ts b/src/mailer/index.ts index f1974349..26ce0973 100644 --- a/src/mailer/index.ts +++ b/src/mailer/index.ts @@ -1,10 +1,10 @@ import nodemailer from "nodemailer" import handlebars from "handlebars" -import { UseMailerProps, WertikApp, WertikConfiguration } from "../types" +import { WithMailerProps, WertikApp, WertikConfiguration } from "../types" import { SendEmailProps } from "../types/mailer" import { wLog, wLogWithSuccess } from "../utils/log" -export const useMailer = (props: UseMailerProps) => { +export const withMailer = (props: WithMailerProps) => { return async () => { let testAccount = props.options ? null diff --git a/src/modules/modules.ts b/src/modules/modules.ts index ce4ea246..f6c73f19 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -1,6 +1,6 @@ import get from "lodash.get" import { databaseDefaultOptions } from "../utils/defaultOptions" -import { RelationParams, UseModuleProps } from "../types/modules" +import { RelationParams, WithModuleProps } from "../types/modules" import { getInsertSchema, getUpdateSchema, @@ -19,7 +19,7 @@ import camelize from "../utils/camelize" * Wertik js module * @param props see interface UseModuleProps */ -export const useModule = (moduleProps: UseModuleProps) => { +export const withModule = (moduleProps: WithModuleProps) => { return async ({ store, configuration, diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 776a6b81..23a9b38f 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -1,5 +1,5 @@ import get from "lodash.get" -import { UseModuleProps } from "../types/modules" +import { WithModuleProps } from "../types/modules" import { TableInfo } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" import crud from "../crud" @@ -63,7 +63,7 @@ export const getGraphQLTypeNameFromSqlType = ( } export const getUpdateSchema = ( - module: UseModuleProps, + module: WithModuleProps, tableInfo: TableInfo ) => { const optionsUpdateSchema = get(module, "graphql.updateSchema", "") @@ -82,7 +82,7 @@ export const getUpdateSchema = ( } export const getInsertSchema = ( - module: UseModuleProps, + module: WithModuleProps, tableInfo: TableInfo ) => { const optionsInsertSchema = get(module, "graphql.createSchema", "") diff --git a/src/queue/index.ts b/src/queue/index.ts index 5e131141..ee9208b3 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -1,7 +1,7 @@ import Queue from "bull" import { isPackageInstalled } from "../utils/checkInstalledPackages" import { WertikApp, WertikConfiguration } from "../types" -import { UseQueueProps } from "./../types/queue" +import { WithQueueProps } from "./../types/queue" import { wLog } from "../utils/log" /** @@ -11,7 +11,7 @@ import { wLog } from "../utils/log" * @returns Queue */ -export const useQueue = (props: UseQueueProps) => { +export const withQueue = (props: WithQueueProps) => { return () => new Queue(props.name, props.url, props.options) } diff --git a/src/redis/index.ts b/src/redis/index.ts index 4335548c..2be7dd11 100644 --- a/src/redis/index.ts +++ b/src/redis/index.ts @@ -1,8 +1,8 @@ import { createClient } from "redis" import { wLog, wLogWithSuccess } from "../utils/log" -import { UseRedisProps, WertikApp, WertikConfiguration } from "../types" +import { WithRedisProps, WertikApp, WertikConfiguration } from "../types" -export const useRedis = (props?: UseRedisProps) => { +export const withRedis = (props?: WithRedisProps) => { return async ({ configuration, wertikApp, diff --git a/src/sockets/index.ts b/src/sockets/index.ts index bad09f00..4adcceca 100644 --- a/src/sockets/index.ts +++ b/src/sockets/index.ts @@ -14,7 +14,7 @@ import { WertikApp, WertikConfiguration } from "../types" * @param props see interface WebSocketServerOptions * @returns WebSocketServer instance */ -export const useWebSockets = (props: WebSocketServerOptions = {}) => { +export const withWebSockets = (props: WebSocketServerOptions = {}) => { return ({ configuration, wertikApp, @@ -41,7 +41,7 @@ export const useWebSockets = (props: WebSocketServerOptions = {}) => { * @param props see interface WebSocketServerOptions * @returns WebSocketServer instance */ -export const useIndependentWebSocketsServer = ( +export const withIndependentWebSocketsServer = ( props: WebSocketServerOptions = {} ) => { return ({ @@ -66,7 +66,7 @@ export const useIndependentWebSocketsServer = ( * @param props see interface SocketIOServerOptions from socket.io * @returns SocketIOServer */ -export const useSocketIO = (props: any = {}) => { +export const withSocketIO = (props: any = {}) => { return ({ configuration, wertikApp, diff --git a/src/types/database.ts b/src/types/database.ts index 0bbcbbde..039052c5 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -1,6 +1,6 @@ import { iObject } from "." -export interface UseMysqlDatabaseProps { +export interface WithMysqlDatabaseProps { /** * Database name */ diff --git a/src/types/graphql.ts b/src/types/graphql.ts index b5e5d822..7b4a9a04 100644 --- a/src/types/graphql.ts +++ b/src/types/graphql.ts @@ -5,7 +5,7 @@ export interface GetMiddlewareOptionsGraphql extends GetMiddlewareOptions { path: string } -export interface UseGraphqlProps { +export interface WithApolloGraphqlProps { options?: { [key: string]: any } diff --git a/src/types/index.ts b/src/types/index.ts index baa847e3..07c0e7b1 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ import { Sequelize } from "sequelize/types" -import { UseMysqlDatabaseProps } from "./database" +import { WithMysqlDatabaseProps } from "./database" import { SendEmailProps } from "./mailer" import { WertikModule } from "./modules" import { ApolloServer } from "apollo-server-express" @@ -44,7 +44,7 @@ export interface WertikConfiguration { httpServer?: iObject /** * [Optional] When passed as true, Wertik will not start server. - * + * * @deprecated Use `selfStart` instead. * @default true */ @@ -60,7 +60,7 @@ export interface WertikConfiguration { */ database?: { [key: string]: () => Promise<{ - credentials: UseMysqlDatabaseProps + credentials: WithMysqlDatabaseProps instance: Sequelize }> } @@ -217,12 +217,12 @@ export interface WertikApp { /** * Provide same options that redis createClient method requires. */ -export interface UseRedisProps { +export interface WithRedisProps { [key: string]: any name: string } -export interface UseMailerProps { +export interface WithMailerProps { /** * Provide name for your mailer. */ diff --git a/src/types/modules.ts b/src/types/modules.ts index e518c51b..690f2fc8 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -39,7 +39,7 @@ export interface RelationParams { } } -export interface UseModuleProps { +export interface WithModuleProps { /** * Your module name. */ diff --git a/src/types/queue.ts b/src/types/queue.ts index 473650c4..f034e402 100644 --- a/src/types/queue.ts +++ b/src/types/queue.ts @@ -1,6 +1,6 @@ import { QueueOptions } from "bull" -export interface UseQueueProps { +export interface WithQueueProps { name: string url?: string options?: QueueOptions diff --git a/tests/database.test.js b/tests/database.test.js index 4a9c62cc..72f4d27e 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -2,9 +2,7 @@ require("dotenv").config() const { default: wertik, - useModule, - useMysqlDatabase, - useGraphql, + withMysqlDatabase, } = require("../lib/index") const { database } = require("./testUtils") @@ -14,7 +12,7 @@ if (database.name) { await expect( wertik({ database: { - default: useMysqlDatabase(database), + default: withMysqlDatabase(database), }, }) ).resolves.not.toThrowError() diff --git a/tests/graphql.test.js b/tests/graphql.test.js index 7b2ac846..fa515ba6 100644 --- a/tests/graphql.test.js +++ b/tests/graphql.test.js @@ -2,27 +2,27 @@ require("dotenv").config() const { default: wertik, - useModule, - useMysqlDatabase, - useGraphql, + withModule, + withMysqlDatabase, + withApolloGraphql, } = require("../lib/index") const { database, Product, User } = require("./testUtils") if (database.name) { - describe("Expect useMysqlDatabase, useModule and useGraphql, and expect module graphql operations work", () => { + describe("Expect withMysqlDatabase, withModule and withApolloGraphql, and expect module graphql operations work", () => { let app test("Expect test database to connect and does not causes error", async () => { await expect( (app = wertik({ database: { - default: useMysqlDatabase(database), + default: withMysqlDatabase(database), }, modules: { - Product: useModule(Product), - User: useModule(User), + Product: withModule(Product), + User: withModule(User), }, - graphql: useGraphql(), + graphql: withApolloGraphql(), }).then((wertikApp) => { app = wertikApp })) diff --git a/tests/index.test.js b/tests/index.test.js index 1f0d3096..c4711ebd 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -2,13 +2,13 @@ require("dotenv").config() const { default: wertik, - useLogger, - useWinstonTransport, - useIndependentWebSocketsServer, - useSocketIO, - useWebSockets, - useMailer, - useGraphql, + withLogger, + withWinstonTransport, + withIndependentWebSocketsServer, + withSocketIO, + withWebSockets, + withMailer, + withApolloGraphql, } = require("./../lib/index") test("Expect no configuration can start the server", async () => { @@ -27,7 +27,7 @@ test("Expect mailer to work without configuration and does not causes error", as await expect( wertik({ mailer: { - default: useMailer({ + default: withMailer({ name: "Default", }), }, @@ -35,25 +35,25 @@ test("Expect mailer to work without configuration and does not causes error", as ).resolves.not.toThrowError() }) -test("Expect graphql to work with useGraphql and does not causes error", async () => { +test("Expect graphql to work with withApolloGraphql and does not causes error", async () => { await expect( wertik({ - graphql: useGraphql(), + graphql: withApolloGraphql(), }) ).resolves.not.toThrowError() }) -test("Expect useWebSockets, useIndependentWebSocketsServer and useSocketIO works and does not throw any error", async () => { +test("Expect withWebSockets, withIndependentWebSocketsServer and withSocketIO works and does not throw any error", async () => { await expect( wertik({ sockets: { - mySockets: useWebSockets({ + mySockets: withWebSockets({ path: "/websockets", }), - socketio: useSocketIO({ + socketio: withSocketIO({ path: "/mysocketioserver", }), - mySockets2: useIndependentWebSocketsServer({ + mySockets2: withIndependentWebSocketsServer({ port: 1500, }), }, @@ -66,8 +66,8 @@ test("Expect useWebSockets, useIndependentWebSocketsServer and useSocketIO works test("Expect logger to run without throwing any error", async () => { await expect( wertik({ - logger: useLogger({ - transports: useWinstonTransport((winston) => { + logger: withLogger({ + transports: withWinstonTransport((winston) => { return [ new winston.transports.File({ filename: "info.log", From 1be79a3fe3da8ad477853f26e7f201757cb2f5be Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Tue, 9 Apr 2024 06:52:22 +0500 Subject: [PATCH 44/52] V3.4 Fetch graphql keys in convertGraphqlRequestedFieldsIntoInclude and renaming of variables --- changelog.md | 3 +++ docs/v3/modules.md | 8 ++++---- package.json | 10 ++++++---- src/crud/index.ts | 9 ++++++--- src/database/eagerLoadingGraphqlQuery.ts | 14 +++++++++----- src/helpers/modules/backup.ts | 12 ++++++------ src/modules/modules.ts | 16 ++++++++-------- src/modules/modulesHelpers.ts | 6 +++++- src/storage/index.ts | 4 ++-- src/types/modules.ts | 14 +++++++------- src/types/storage.ts | 2 +- 11 files changed, 57 insertions(+), 41 deletions(-) diff --git a/changelog.md b/changelog.md index 1eec3223..132f30af 100644 --- a/changelog.md +++ b/changelog.md @@ -31,6 +31,9 @@ - Rename `useWinstonTransport` to `withWinstonTransport` with its props. - Rename `useQueue` to `withQueue` with its props. - Verified tests with Jest for renaming changes. +- Rename `useSchema` to `extendSchema` with its props. +- Rename `useMutation` to `addMutation` with its props. +- Rename `useExpress` to `getExpress`. ### 3.3.0 diff --git a/docs/v3/modules.md b/docs/v3/modules.md index b79bb6a0..a9538632 100644 --- a/docs/v3/modules.md +++ b/docs/v3/modules.md @@ -280,25 +280,25 @@ wertik({ name: "Games", table: "games", database: "jscontainer", - on({ useExpress, useQuery, useMutation, useSchema }) { + on({ useExpress, addQuery, addMutation, extendSchema }) { useExpress((express) => { express.get("/404", (req, res) => res.status(404).send("404")); }); - useQuery({ + addQuery({ name: "getGames", query: "getGames: [Games]", resolver() { return []; }, }); - useMutation({ + addMutation({ name: "updateAllGames", query: "updateAllGames: [Games]", resolver() { return []; }, }); - useSchema(` + extendSchema(` type MyType { id: Int name: String diff --git a/package.json b/package.json index 8b3f1d31..b342c4c4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "wertik-js", - "version": "3.3.3", - "main": "lib/main.js", + "version": "3.4.0", + "main": "lib/index.js", "types": "lib/types.d.ts", "repository": "https://github.com/Uconnect-Technologies/wertik-js.git", "keywords": [ @@ -26,9 +26,11 @@ "license": "MIT", "scripts": { "dev": "tsc-watch --onSuccess \"node lib/devServer.js\"", - "build": "yarn prettier && yarn tsc && yarn test", + "build": "rm -rf lib && yarn prettier && yarn tsc && yarn test", "prettier": "prettier --write src package.json index.js", - "test": "cross-env TEST_MODE=true jest --runInBand --forceExit --detectOpenHandles" + "test": "cross-env TEST_MODE=true jest --runInBand --forceExit --detectOpenHandles", + "preinstall": "rm -rf node_modules", + "prepare": "yarn tsc" }, "pre-commit": [ "prettier" diff --git a/src/crud/index.ts b/src/crud/index.ts index 6841af28..eef8e99a 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -207,7 +207,8 @@ export default function (module, schemaInformation, store) { ), include: convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }), - args + args, + module ), }) @@ -234,7 +235,8 @@ export default function (module, schemaInformation, store) { schemaInformation.tableInstance, convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }), - args + args, + module ), { attributes: generateRequestedFieldsFromGraphqlInfo( @@ -269,7 +271,8 @@ export default function (module, schemaInformation, store) { where: omit(where, keys), include: convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }), - args + args, + module ), }) return count diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 6805a4d9..5acaea27 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -28,13 +28,14 @@ const clean = (cleanObject) => { export const convertGraphqlRequestedFieldsIntoInclude = ( graphqlFields = {}, - args: any = {} + args: any = {}, + module: any = {} ) => { graphqlFields = clean(graphqlFields) - const keys = [ - ...store.database.relationships.map((c) => c.graphqlKey), - ...store.graphql.graphqlKeys, - ] + const keys = store.database.relationships + .filter((f) => f.currentModule == module.name) + .map((c) => c.graphqlKey) + const requiredFilters = keys.filter((c) => Object.keys(args.where ?? {}).includes(c) ) @@ -83,6 +84,9 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let include = recursion(graphqlFields) + console.log(include) + console.log(keys) + /** * Make sure the include is required if filters are requested in root level filters. * If root level filters are not met then the response will be null. diff --git a/src/helpers/modules/backup.ts b/src/helpers/modules/backup.ts index 0a16617b..25024585 100644 --- a/src/helpers/modules/backup.ts +++ b/src/helpers/modules/backup.ts @@ -79,15 +79,15 @@ export const WertikBackupModule = ( database: database, table: table, tableOptions: tableOptions, - on: function ({ useSchema, useMutation }) { - useSchema(` + on: function ({ extendSchema, addMutation }) { + extendSchema(` type BackupSuccessResponse { message: String filename: String - backup: Backup + backup: BackupModule } `) - useMutation({ + addMutation({ name: "backupLocal", query: "backupLocal(database: [String]!): [BackupSuccessResponse]", async resolver(_, args, context) { @@ -108,7 +108,7 @@ export const WertikBackupModule = ( return push }, }) - useMutation({ + addMutation({ name: "backupDigitalOceanSpaces", query: "backupDigitalOceanSpaces(ACL: String!, Bucket: String!, storage: String!, database: [String]!): [BackupSuccessResponse]", @@ -152,7 +152,7 @@ export const WertikBackupModule = ( } }, }) - useMutation({ + addMutation({ name: "backupDropbox", query: "backupDropbox(storage: String!, database: [String]): [BackupSuccessResponse]", diff --git a/src/modules/modules.ts b/src/modules/modules.ts index f6c73f19..f965b859 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -48,13 +48,13 @@ export const withModule = (moduleProps: WithModuleProps) => { ) } - const useSchema = (string: string) => { + const extendSchema = (string: string) => { store.graphql.typeDefs = store.graphql.typeDefs.concat(` ${string} `) } - const useQuery = ({ query, resolver, name }) => { + const addQuery = ({ query, resolver, name }) => { store.graphql.typeDefs = store.graphql.typeDefs.concat(` extend type Query { ${query} @@ -63,7 +63,7 @@ export const withModule = (moduleProps: WithModuleProps) => { store.graphql.resolvers.Query[name] = resolver } - const useMutation = ({ query, resolver, name }) => { + const addMutation = ({ query, resolver, name }) => { store.graphql.typeDefs = store.graphql.typeDefs.concat(` extend type Mutation { ${query} @@ -72,7 +72,7 @@ export const withModule = (moduleProps: WithModuleProps) => { store.graphql.resolvers.Mutation[name] = resolver } - const useExpress = (fn = (express) => {}) => { + const getExpress = (fn = (express) => {}) => { setTimeout(() => { fn(app.express) }, 2500) @@ -171,14 +171,14 @@ export const withModule = (moduleProps: WithModuleProps) => { ) } get(moduleProps, "on", () => {})({ - useQuery, - useMutation, - useExpress, + addQuery, + addMutation, + getExpress, hasOne, belongsTo, belongsToMany, hasMany, - useSchema, + extendSchema, }) let insertSchema = [] diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 23a9b38f..37ac4708 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -171,5 +171,9 @@ export const generateRowFieldNameForModuleName = (moduleName) => { return snackCase(pluralize.singular(moduleName)).toLowerCase() } export const generateRowsFieldNameForModuleName = (moduleName) => { - return snackCase(pluralize.plural(moduleName)).toLowerCase() + let fieldName = snackCase(pluralize.plural(moduleName)).toLowerCase() + if (generateRowFieldNameForModuleName(moduleName) == fieldName) { + return fieldName + "s" + } + return fieldName } diff --git a/src/storage/index.ts b/src/storage/index.ts index 1dedb68d..437dc102 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -1,11 +1,11 @@ import { WertikApp, WertikConfiguration } from "../types" -import { UseStorageProps } from "../types/storage" +import { WithStorageProps } from "../types/storage" import { wLog } from "../utils/log" const DIGITAL_OCEAN = "digitalocean" const DROPBOX = "dropbox" -export const useStorage = (storageItem: UseStorageProps) => { +export const withStorage = (storageItem: WithStorageProps) => { return ({ configuration, wertikApp, diff --git a/src/types/modules.ts b/src/types/modules.ts index 690f2fc8..cac81cd2 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -1,7 +1,7 @@ import { iObject } from "." import { ModelStatic, Model } from "sequelize/types" -export interface UseQueryProps { +export interface AddQueryProps { /** * Schema for this query, for example: getUsers: [Users] */ @@ -15,7 +15,7 @@ export interface UseQueryProps { */ name: string } -export interface UseMutationProps { +export interface AddMutationProps { /** * Schema for this query, for example: deleteUsers: Boolean */ @@ -29,7 +29,7 @@ export interface UseMutationProps { */ name: string } -export type useExpressProps = Function +export type GetExpressProps = Function export interface RelationParams { module: string graphqlKey: string @@ -109,15 +109,15 @@ export interface WithModuleProps { /** * This Method allows you adding graphql query to your module. */ - useQuery: (props: UseQueryProps) => {} | void + addQuery: (props: AddQueryProps) => {} | void /** * This Method allows you adding graphql mutation to your module. */ - useMutation: (props: UseMutationProps) => {} | void + addMutation: (props: AddMutationProps) => {} | void /** * This method gives you access to express app instance. */ - useExpress: (express: any) => void + getExpress: (express: any) => void /** * This method adds a one-to-one relationship to a module. */ @@ -137,7 +137,7 @@ export interface WithModuleProps { /** * This method adds has many relationship to a module. */ - useSchema: (props: string) => {} | void + extendSchema: (props: string) => {} | void }) => void /** * Graphql events when a CRUD operation happens. diff --git a/src/types/storage.ts b/src/types/storage.ts index f70a4faa..16681c43 100644 --- a/src/types/storage.ts +++ b/src/types/storage.ts @@ -1,4 +1,4 @@ -export interface UseStorageProps { +export interface WithStorageProps { for: "dropbox" | "digitalocean" name: string dropboxOptions?: { From b18783cb3d1a1100b54b4c671b75f99a5f4a0dda Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Wed, 10 Apr 2024 11:34:34 +0500 Subject: [PATCH 45/52] v3.4 replace sorting with order and allow deep sorting --- src/crud/index.ts | 19 +++++----- src/crud/paginate.ts | 7 ++-- src/database/eagerLoadingGraphqlQuery.ts | 22 ++++++++---- src/graphql/generalSchema.ts | 5 +++ src/modules/modules.ts | 15 ++++---- src/modules/modulesHelpers.ts | 45 ++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 24 deletions(-) diff --git a/src/crud/index.ts b/src/crud/index.ts index eef8e99a..c713dbf7 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -2,6 +2,7 @@ import get from "lodash.get" import { wLogWithDateWithInfo } from "../utils/log" import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" import { + generateOrderByForModule, generateRequestedFieldsFromGraphqlInfo, generateRowFieldNameForModuleName, generateRowsFieldNameForModuleName, @@ -23,7 +24,6 @@ export default function (module, schemaInformation, store) { type ${module.name}List { rows: [${module.name}Module] pagination: Pagination - sorting: Sorting paginationProperties: PaginationProperties @deprecated(reason: "Use pagination instead") } type ${module.name}BulkMutationResponse { @@ -36,7 +36,7 @@ export default function (module, schemaInformation, store) { extend type Query { ${singleRowFieldName}(where: ${singleRowFieldName}_filter_input): ${module.name}Module - ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, sorting: [SortingInput]): ${module.name}List + ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, order: ${generateRowFieldNameForModuleName(module.name)}_order_input): ${module.name}List count${module.name}(where: ${singleRowFieldName}_filter_input): Int }` }, @@ -230,19 +230,22 @@ export default function (module, schemaInformation, store) { )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args + const convertFieldsIntoInclude = convertGraphqlRequestedFieldsIntoInclude( + graphqlFields(info, {}, { processArguments: true }), + args, + module + ) + return await paginate( args, schemaInformation.tableInstance, - convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info, {}, { processArguments: true }), - args, - module - ), + convertFieldsIntoInclude.include, { attributes: generateRequestedFieldsFromGraphqlInfo( graphqlFields(info).rows ), - } + }, + generateOrderByForModule(module, [[args.order], ...convertFieldsIntoInclude.order],) ) } ), diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 29cf5376..00d21410 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -1,15 +1,16 @@ import store from "../store" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import omit from "lodash.omit" +import isPlainObject from "lodash.isplainobject" export const paginate = async ( arg, tableInstance, includes: any[] = [], - queryOptions: { [key: string]: any } = {} + queryOptions: { [key: string]: any } = {}, + order = [] ) => { const { page = 1, limit = 100 } = arg.pagination ?? {} - const sorting = arg.sorting ?? [] const offset = limit * (page - 1) const keys = [ ...store.database.relationships.map((c) => c.graphqlKey), @@ -21,7 +22,7 @@ export const paginate = async ( where, offset, limit, - order: sorting.map(({ column, type }) => [column, type]), + order, include: includes, ...queryOptions, }) diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 5acaea27..63bea98e 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -31,11 +31,15 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( args: any = {}, module: any = {} ) => { + let order = []; + let depth = [] graphqlFields = clean(graphqlFields) const keys = store.database.relationships .filter((f) => f.currentModule == module.name) .map((c) => c.graphqlKey) + const allRelationshipKeys = store.database.relationships.map((c) => c.graphqlKey) + const requiredFilters = keys.filter((c) => Object.keys(args.where ?? {}).includes(c) ) @@ -44,7 +48,7 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let includes = [] for (const key in _obj) { - if (keys.includes(key)) { + if (allRelationshipKeys.includes(key)) { const includeParams: { [key: string]: any } = { required: false, model: @@ -60,10 +64,16 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let __arguments = get(_obj, `[${key}].__arguments`, []) let __whereInArguments = __arguments.find((c) => has(c, "where")) + let __orderInArguments = __arguments.find((c) => has(c, "order")) let __limitInArguments = __arguments.find((c) => has(c, "limit")) let __offsetInArguments = __arguments.find((c) => has(c, "offset")) __limitInArguments = get(__limitInArguments, "limit.value", null) __offsetInArguments = get(__offsetInArguments, "offset.value", null) + __orderInArguments = get(__orderInArguments, "order.value", null) + + if (__orderInArguments) { + order.push([...depth, key, __orderInArguments]) + } if (__whereInArguments) { __whereInArguments = get(__whereInArguments, "where.value", {}) @@ -75,7 +85,6 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( if (__limitInArguments) includeParams.limit = __limitInArguments if (__offsetInArguments) includeParams.offset = __offsetInArguments - includes.push(includeParams) } } @@ -83,10 +92,6 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( } let include = recursion(graphqlFields) - - console.log(include) - console.log(keys) - /** * Make sure the include is required if filters are requested in root level filters. * If root level filters are not met then the response will be null. @@ -107,5 +112,8 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( return c }) - return include + return { + include, + order, + } } diff --git a/src/graphql/generalSchema.ts b/src/graphql/generalSchema.ts index 0df407b0..56c5fd32 100644 --- a/src/graphql/generalSchema.ts +++ b/src/graphql/generalSchema.ts @@ -6,6 +6,11 @@ export default ` scalar JSON scalar JSONObject + enum order_by { + ASC + DESC + } + input string_filter_input { _eq: String _ne: String diff --git a/src/modules/modules.ts b/src/modules/modules.ts index f965b859..4d46c292 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -8,6 +8,7 @@ import { generateGenerateGraphQLCrud, generateRowsFieldNameForModuleName, generateRowFieldNameForModuleName, + getOrderSchema, } from "./modulesHelpers" import { getMysqlTableInfo } from "../database/mysql/getTableInfo" import { Store, WertikApp, WertikConfiguration } from "./../types/index" @@ -38,6 +39,7 @@ export const withModule = (moduleProps: WithModuleProps) => { moduleProps.name )}_filter_input {`, ] + let orderSchema = "" const useDatabase = get(moduleProps, "useDatabase", false) @@ -119,12 +121,11 @@ export const withModule = (moduleProps: WithModuleProps) => { ) } const belongsToMany = (params: RelationParams) => { + let field_name = generateRowFieldNameForModuleName(params.module) graphqlSchema.push( `${ params.graphqlKey - }(offset: Int, limit: Int, where: ${generateRowFieldNameForModuleName( - params.module - )}_filter_input, sorting: [SortingInput]): [${params.module}Module]` + }(offset: Int, limit: Int, where: ${field_name}_filter_input, order: ${field_name}_order_input): [${params.module}Module]` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -145,12 +146,11 @@ export const withModule = (moduleProps: WithModuleProps) => { ) } const hasMany = (params: RelationParams) => { + let field_name = generateRowFieldNameForModuleName(params.module) graphqlSchema.push( `${ params.graphqlKey - }(offset: Int, limit: Int, where: ${generateRowFieldNameForModuleName( - params.module - )}_filter_input, sorting: [SortingInput]): [${params.module}Module]` + }(offset: Int, limit: Int, where: ${field_name}_filter_input, order: ${field_name}_order_input): [${params.module}Module]` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -236,6 +236,8 @@ export const withModule = (moduleProps: WithModuleProps) => { insertSchema = getInsertSchema(moduleProps, tableInfo) + orderSchema = getOrderSchema(moduleProps, tableInfo) + tableInfo.columns.forEach((column) => { let filter_input = column.databaseType.toLowerCase() === "enum" @@ -268,6 +270,7 @@ export const withModule = (moduleProps: WithModuleProps) => { update: updateSchema || "", list: listSchema, filters: filterSchema.join("\n"), + order_schema: orderSchema || "", }, } diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 37ac4708..bcc02136 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -6,6 +6,8 @@ import crud from "../crud" import store from "../store" import pluralize from "pluralize" import snackCase from "lodash.snakecase" +import isPlainObject from "lodash.isplainobject" +import { print } from "util" export const generateDataTypeFromDescribeTableColumnType = (Type: string) => { let length = Type.match(/[0-9]/g)?.join("") @@ -101,6 +103,22 @@ export const getInsertSchema = ( return insertSchema.join("\n") } +export const getOrderSchema = (module: WithModuleProps, tableInfo) => { + let orderSchema = [ + `input ${generateRowFieldNameForModuleName(module.name)}_order_input {`, + ] + let relationships = store.database.relationships.filter( + (c) => c.currentModule === module.name + ) + tableInfo.columns.forEach((column) => { + orderSchema.push(`${column.columnName}: order_by`) + }) + + orderSchema.push("}") + + return orderSchema.join("\n") +} + export const generateEnumTypeForGraphql = (column: TableInfo["columns"][0]) => { return `enum ${column.graphqlType} { ${column.enumValues.join("\n")} @@ -120,6 +138,7 @@ export const generateGenerateGraphQLCrud = ( \n ${schemaInformation.inputSchema.filters} \n ${schemaInformation.inputSchema.insert} \n ${schemaInformation.inputSchema.update} + \n ${schemaInformation.inputSchema.order_schema} ` ) @@ -177,3 +196,29 @@ export const generateRowsFieldNameForModuleName = (moduleName) => { } return fieldName } + +export const generateOrderByForModule = (module, order) => { + const final_order = []; + const currentModuleRelationships = store.database.relationships + .filter((c) => c.currentModule === module.name) + .map((c) => { + return c.options.as + }) + + order.forEach(element => { + element.forEach(sub_element => { + if (isPlainObject(sub_element)) { + Object.keys(sub_element).forEach(key => { + final_order.push([ + ...element.filter((c) => !isPlainObject(c)), + key, + sub_element[key] + ]) + }) + } + }); + }); + + + return final_order +} From 16e17dd7e0518edd8238742e50f773edd6f7b769 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sat, 13 Apr 2024 12:42:55 +0500 Subject: [PATCH 46/52] v3.4 Added sorting for deep level --- src/crud/index.ts | 3 +- src/database/eagerLoadingGraphqlQuery.ts | 48 +++++++++++++++--------- src/devServer.ts | 14 +++---- src/index.ts | 3 ++ src/modules/modules.ts | 31 +++++++++++---- src/modules/modulesHelpers.ts | 26 ------------- src/store.ts | 21 +++++++++-- src/types/index.ts | 5 ++- src/types/modules.ts | 3 +- src/utils/log.ts | 6 ++- 10 files changed, 94 insertions(+), 66 deletions(-) diff --git a/src/crud/index.ts b/src/crud/index.ts index c713dbf7..3e3166ec 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -2,7 +2,6 @@ import get from "lodash.get" import { wLogWithDateWithInfo } from "../utils/log" import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" import { - generateOrderByForModule, generateRequestedFieldsFromGraphqlInfo, generateRowFieldNameForModuleName, generateRowsFieldNameForModuleName, @@ -245,7 +244,7 @@ export default function (module, schemaInformation, store) { graphqlFields(info).rows ), }, - generateOrderByForModule(module, [[args.order], ...convertFieldsIntoInclude.order],) + convertFieldsIntoInclude.order, ) } ), diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index 63bea98e..ea63b27b 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -32,30 +32,42 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( module: any = {} ) => { let order = []; - let depth = [] + let depth = []; graphqlFields = clean(graphqlFields) - const keys = store.database.relationships - .filter((f) => f.currentModule == module.name) - .map((c) => c.graphqlKey) + const currentModuleRelationships = store.database.relationships.filter( + (f) => f.currentModule == module.name + ) - const allRelationshipKeys = store.database.relationships.map((c) => c.graphqlKey) + const currentModuleRelationshipsKeys = currentModuleRelationships.map( + (c) => c.graphqlKey + ) + const allRelationshipKeys = store.database.relationships.map( + (c) => c.graphqlKey + ) - const requiredFilters = keys.filter((c) => + const requiredFilters = currentModuleRelationshipsKeys.filter((c) => Object.keys(args.where ?? {}).includes(c) ) + + Object.keys(args.order ?? {}).forEach((element) => { + order.push([element, args.order[element]]) + }) + let recursion = (_obj) => { let includes = [] - for (const key in _obj) { + Object.keys(_obj).forEach((key) => { if (allRelationshipKeys.includes(key)) { + depth.push(key) + let _localDepth = [...JSON.parse(JSON.stringify(depth))] + const relationship = store.database.relationships.find( + (c) => c.graphqlKey === key + ) + const sequelizeModel = wertikApp.models[relationship.referencedModule] const includeParams: { [key: string]: any } = { required: false, - model: - wertikApp.models[ - store.database.relationships.find((c) => c.graphqlKey === key) - .referencedModule - ], + model: sequelizeModel, as: key, attributes: generateRequestedFieldsFromGraphqlInfo(_obj[key]), include: @@ -71,8 +83,10 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( __offsetInArguments = get(__offsetInArguments, "offset.value", null) __orderInArguments = get(__orderInArguments, "order.value", null) - if (__orderInArguments) { - order.push([...depth, key, __orderInArguments]) + if (isPlainObject(__orderInArguments)) { + Object.keys(__orderInArguments).forEach((element) => { + order.push([..._localDepth, element, __orderInArguments[element]]) + }) } if (__whereInArguments) { @@ -87,20 +101,20 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( if (__offsetInArguments) includeParams.offset = __offsetInArguments includes.push(includeParams) } - } + }) return includes } let include = recursion(graphqlFields) /** - * Make sure the include is required if filters are requested in root level filters. + * Make sure the include is required if filters are requested in root level filters. * If root level filters are not met then the response will be null. * In below graphql query, it will return if user has id 2 and written a post which id is 132, if id is not found then whole response will be null. query viewUser { viewUser(where: { id: { _eq: 2 }, posts: { id: { _eq: 123 } } }) { id name - } + } } */ include = include.map((c) => { diff --git a/src/devServer.ts b/src/devServer.ts index 75f31b86..5c58259e 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -163,13 +163,13 @@ wertik({ ] }), }), - mailer: { - instances: { - default: withMailer({ - name: "Default", - }), - }, - }, + // mailer: { + // instances: { + // default: withMailer({ + // name: "Default", + // }), + // }, + // }, redis: { testRedis: withRedis({ name: "testRedis", diff --git a/src/index.ts b/src/index.ts index f040ddc5..528dd028 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import http from "http" import { WertikConfiguration, WertikApp } from "./types" import { initializeBullBoard } from "./queue/index" import { wLogWithInfo, wLogWithSuccess } from "./utils/log" +import { validateModules } from "./modules/modules" export * from "./database/database" export * from "./modules/modules" @@ -164,6 +165,8 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( next() }) + validateModules(wertikApp) + let startServer = () => { httpServer.listen(port, () => { wLogWithSuccess(`[Wertik-App]`, `http://localhost:${port}`) diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 4d46c292..c98cd401 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -13,7 +13,12 @@ import { import { getMysqlTableInfo } from "../database/mysql/getTableInfo" import { Store, WertikApp, WertikConfiguration } from "./../types/index" import { ModelStatic, Model, ModelAttributes } from "sequelize/types" -import { wLogWithInfo } from "../utils/log" +import { + wLogWithError, + wLogWithInfo, + wLogWithSuccess, + wLogWithWarn, +} from "../utils/log" import camelize from "../utils/camelize" /** @@ -30,6 +35,7 @@ export const withModule = (moduleProps: WithModuleProps) => { configuration: WertikConfiguration app: WertikApp }) => { + store.modules.push(moduleProps) let currentModuleRelationships = [] let tableInstance: ModelStatic> let graphqlSchema = [`type ${moduleProps.name}Module {`] @@ -123,9 +129,7 @@ export const withModule = (moduleProps: WithModuleProps) => { const belongsToMany = (params: RelationParams) => { let field_name = generateRowFieldNameForModuleName(params.module) graphqlSchema.push( - `${ - params.graphqlKey - }(offset: Int, limit: Int, where: ${field_name}_filter_input, order: ${field_name}_order_input): [${params.module}Module]` + `${params.graphqlKey}(offset: Int, limit: Int, where: ${field_name}_filter_input, order: ${field_name}_order_input): [${params.module}Module]` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -148,9 +152,7 @@ export const withModule = (moduleProps: WithModuleProps) => { const hasMany = (params: RelationParams) => { let field_name = generateRowFieldNameForModuleName(params.module) graphqlSchema.push( - `${ - params.graphqlKey - }(offset: Int, limit: Int, where: ${field_name}_filter_input, order: ${field_name}_order_input): [${params.module}Module]` + `${params.graphqlKey}(offset: Int, limit: Int, where: ${field_name}_filter_input, order: ${field_name}_order_input): [${params.module}Module]` ) let relationshipInfo = { currentModule: moduleProps.name, @@ -265,6 +267,7 @@ export const withModule = (moduleProps: WithModuleProps) => { moduleName: moduleProps.name, tableInstance: tableInstance, schema: graphqlSchema.join(`\n`), + props: moduleProps, inputSchema: { insert: insertSchema || "", update: updateSchema || "", @@ -284,3 +287,17 @@ export const withModule = (moduleProps: WithModuleProps) => { return schemaInformation } } + +export function validateModules(wertikApp: WertikApp) { + wLogWithInfo("Validating:", "Modules") + Object.keys(wertikApp.modules).forEach((name) => { + let module = wertikApp.modules[name] + if (name !== module.props.name) { + wLogWithError( + "[MODULE NAME CONFLICT]", + "Please use same name for both key and module name" + ) + process.exit() + } + }) +} diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index bcc02136..e6e3b8d2 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -196,29 +196,3 @@ export const generateRowsFieldNameForModuleName = (moduleName) => { } return fieldName } - -export const generateOrderByForModule = (module, order) => { - const final_order = []; - const currentModuleRelationships = store.database.relationships - .filter((c) => c.currentModule === module.name) - .map((c) => { - return c.options.as - }) - - order.forEach(element => { - element.forEach(sub_element => { - if (isPlainObject(sub_element)) { - Object.keys(sub_element).forEach(key => { - final_order.push([ - ...element.filter((c) => !isPlainObject(c)), - key, - sub_element[key] - ]) - }) - } - }); - }); - - - return final_order -} diff --git a/src/store.ts b/src/store.ts index f029fd6f..e520906a 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,5 +1,18 @@ import generalSchema from "./graphql/generalSchema" import { WertikApp } from "./types" +import { WithModuleProps } from "./types/modules" + + +type StoreDatabaseRelationship = { + currentModule: string, + currentModuleDatabase: string, + graphqlKey: string, + referencedModule: string, + referencedModuleDatabase: string, + options: { + [key: string]: unknown + } +} /** * @description This is the store of the app. It contains all the data that is required by the app to run. @@ -38,16 +51,17 @@ const store: { [key: string]: Function } [key: string]: { - [key: string]: Function + [key: string]: Function | string | number | boolean | object | any } } } database: { - relationships: any[] + relationships: StoreDatabaseRelationship[] models: { [key: string]: any } - } + }, + modules: WithModuleProps[] } = { graphql: { graphqlKeys: [], @@ -81,6 +95,7 @@ const store: { relationships: [], models: {}, }, + modules: [] } export default store diff --git a/src/types/index.ts b/src/types/index.ts index 07c0e7b1..85bb2949 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,7 +1,7 @@ import { Sequelize } from "sequelize/types" import { WithMysqlDatabaseProps } from "./database" import { SendEmailProps } from "./mailer" -import { WertikModule } from "./modules" +import { WertikModule, WithModuleProps } from "./modules" import { ApolloServer } from "apollo-server-express" export type iObject = { [key: string]: any } @@ -21,7 +21,8 @@ export interface Store { } database: { relationships: Array - } + }, + modules: WithModuleProps[] } export interface WertikConfiguration { diff --git a/src/types/modules.ts b/src/types/modules.ts index cac81cd2..dadc3985 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -182,5 +182,6 @@ export interface WertikModule { update: string | any[] list: string filters: string - } + }, + props: WithModuleProps } diff --git a/src/utils/log.ts b/src/utils/log.ts index 8aab7a64..c0761cd0 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -21,5 +21,9 @@ export const wLogWithError = (info, ...params) => { } export const wLogWithSuccess = (info, ...params) => { - console.log(chalk.greenBright(info), ...params) + console.log(chalk.green(info), ...params) } + +export const wLogWithWarn = (info, ...params) => { + console.log(chalk.bgYellow.bold("WARN"), info, ...params) +} \ No newline at end of file From 6f5ed59a4f903586c2d2ecbe7dc599e9dd782bcc Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sat, 13 Apr 2024 15:00:34 +0500 Subject: [PATCH 47/52] v3.4 fix tests and finalize order --- package.json | 1 + src/crud/index.ts | 40 +- src/database/eagerLoadingGraphqlQuery.ts | 5 +- src/devServer.ts | 97 +---- src/graphql/index.ts | 7 +- src/modules/modules.ts | 9 + src/store.ts | 15 +- src/types/graphql.ts | 3 + src/types/index.ts | 2 +- src/types/modules.ts | 2 +- src/utils/log.ts | 2 +- test_database.sql | 82 ++-- tests/graphql.test.js | 12 +- wertik-test-database/category.json | 3 + wertik-test-database/product.json | 3 + wertik-test-database/user.json | 522 +++++++++++++++++++++++ wertik.sql | 56 +++ 17 files changed, 698 insertions(+), 163 deletions(-) create mode 100644 wertik-test-database/category.json create mode 100644 wertik-test-database/product.json create mode 100644 wertik-test-database/user.json create mode 100644 wertik.sql diff --git a/package.json b/package.json index b342c4c4..a1ef2cd1 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "dropbox": "^8.2.0", "express": "^4.17.1", "graphql": "^15.7.2", + "graphql-depth-limit": "^1.1.0", "graphql-fields": "^2.0.3", "graphql-type-json": "^0.3.2", "handlebars": "^4.5.3", diff --git a/src/crud/index.ts b/src/crud/index.ts index 3e3166ec..90365d92 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -34,8 +34,12 @@ export default function (module, schemaInformation, store) { } extend type Query { - ${singleRowFieldName}(where: ${singleRowFieldName}_filter_input): ${module.name}Module - ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, order: ${generateRowFieldNameForModuleName(module.name)}_order_input): ${module.name}List + ${singleRowFieldName}(where: ${singleRowFieldName}_filter_input): ${ + module.name + }Module + ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, order: ${generateRowFieldNameForModuleName( + module.name + )}_order_input): ${module.name}List count${module.name}(where: ${singleRowFieldName}_filter_input): Int }` }, @@ -199,16 +203,20 @@ export default function (module, schemaInformation, store) { args.where ) + const convertFieldsIntoInclude = + convertGraphqlRequestedFieldsIntoInclude( + graphqlFields(info, {}, { processArguments: true }), + args, + module + ) + const find = await schemaInformation.tableInstance.findOne({ where: omit(where, keys), attributes: generateRequestedFieldsFromGraphqlInfo( graphqlFields(info) ), - include: convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info, {}, { processArguments: true }), - args, - module - ), + include: convertFieldsIntoInclude.include, + order: convertFieldsIntoInclude.order, }) return find @@ -229,11 +237,12 @@ export default function (module, schemaInformation, store) { )(_, args, context, info) args = argsFromEvent ? argsFromEvent : args - const convertFieldsIntoInclude = convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info, {}, { processArguments: true }), - args, - module - ) + const convertFieldsIntoInclude = + convertGraphqlRequestedFieldsIntoInclude( + graphqlFields(info, {}, { processArguments: true }), + args, + module + ) return await paginate( args, @@ -244,7 +253,7 @@ export default function (module, schemaInformation, store) { graphqlFields(info).rows ), }, - convertFieldsIntoInclude.order, + convertFieldsIntoInclude.order ) } ), @@ -271,11 +280,6 @@ export default function (module, schemaInformation, store) { ] const count = await schemaInformation.tableInstance.count({ where: omit(where, keys), - include: convertGraphqlRequestedFieldsIntoInclude( - graphqlFields(info, {}, { processArguments: true }), - args, - module - ), }) return count } diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index ea63b27b..faa4b6e4 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -31,8 +31,8 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( args: any = {}, module: any = {} ) => { - let order = []; - let depth = []; + let order = [] + let depth = [] graphqlFields = clean(graphqlFields) const currentModuleRelationships = store.database.relationships.filter( (f) => f.currentModule == module.name @@ -49,7 +49,6 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( Object.keys(args.where ?? {}).includes(c) ) - Object.keys(args.order ?? {}).forEach((element) => { order.push([element, args.order[element]]) }) diff --git a/src/devServer.ts b/src/devServer.ts index 5c58259e..413003ce 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -17,16 +17,9 @@ wertik({ storeTypeDefFilePath: process.cwd() + "/graphqlSchema.graphql", }), database: { - ecommerce: withMysqlDatabase({ + wertik: withMysqlDatabase({ port: 3306, - name: "wertik", - host: "127.0.0.1", - password: "pass", - username: "root", - }), - wapgee_prod: withMysqlDatabase({ - port: 3306, - name: "wapgee_prod", + name: "wertik_test", host: "127.0.0.1", password: "pass", username: "root", @@ -36,11 +29,11 @@ wertik({ Product: withModule({ name: "Product", useDatabase: true, - database: "ecommerce", + database: "wertik", table: "product", on: function ({ belongsTo }) { belongsTo({ - database: "ecommerce", + database: "wertik", graphqlKey: "user", module: "User", options: { @@ -54,11 +47,11 @@ wertik({ User: withModule({ name: "User", useDatabase: true, - database: "ecommerce", + database: "wertik", table: "user", on: function ({ hasMany }) { hasMany({ - database: "ecommerce", + database: "wertik", graphqlKey: "products", module: "Product", options: { @@ -69,78 +62,12 @@ wertik({ }) }, }), - // EcommerceShirts: withModule({ - // name: "EcommerceShirts", - // useDatabase: true, - // database: "ecommerce", - // table: "shirts", - // on: function ({ belongsTo }) { - // belongsTo({ - // database: "ecommerce", - // graphqlKey: "user", - // module: "EcommerceUsers", - // options: { - // as: "user", - // foreignKey: "user_id", - // targetKey: "id", - // }, - // }) - // }, - // }), - // EcommerceUsers: withModule({ - // name: "EcommerceUsers", - // useDatabase: true, - // database: "ecommerce", - // table: "users", - // on: function ({ hasMany }) { - // hasMany({ - // database: "ecommerce", - // graphqlKey: "shirts", - // module: "EcommerceShirts", - // options: { - // as: "shirts", - // foreignKey: "user_id", - // sourceKey: "id", - // }, - // }) - // }, - // }), - // User: withModule({ - // name: "User", - // useDatabase: true, - // table: "users", - // database: "wapgee_prod", - // on: function ({ hasMany }) { - // hasMany({ - // database: "wapgee_prod", - // graphqlKey: "posts", - // module: "Post", - // options: { - // as: "posts", - // foreignKey: "created_by", - // sourceKey: "id", - // }, - // }) - // }, - // }), - // Post: withModule({ - // name: "Post", - // useDatabase: true, - // table: "post", - // database: "wapgee_prod", - // on: function ({ hasOne }) { - // hasOne({ - // module: "User", - // graphqlKey: "author", - // database: "default", - // options: { - // as: "author", - // sourceKey: "created_by", - // foreignKey: "id", - // }, - // }) - // }, - // }), + Category: withModule({ + name: "Category", + useDatabase: true, + database: "wertik", + table: "category", + }), }, sockets: { mySockets: withWebSockets({ diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 459510d8..46fe7c3e 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -1,14 +1,15 @@ +import fs from "fs" import get from "lodash.get" import omit from "lodash.omit" import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" +import graphqlDepthLimit from "graphql-depth-limit" + import { WithApolloGraphqlProps, GraphqlInitializeProps, } from "../types/graphql" import { wLogWithSuccess } from "../utils/log" -import fs from "fs" -import path from "path" export const withApolloGraphql = (props?: WithApolloGraphqlProps) => { return ({ @@ -17,6 +18,7 @@ export const withApolloGraphql = (props?: WithApolloGraphqlProps) => { store, configuration, }: GraphqlInitializeProps) => { + const depthLimit = get(props, "validation.depthLimit", 7) props = props ? props : {} store.graphql.typeDefs = store.graphql.typeDefs.concat( get(configuration, "graphql.typeDefs", "") @@ -57,6 +59,7 @@ export const withApolloGraphql = (props?: WithApolloGraphqlProps) => { ...contextFromOptions, } }, + validationRules: [graphqlDepthLimit(depthLimit)], }) GraphqlApolloServer.applyMiddleware({ diff --git a/src/modules/modules.ts b/src/modules/modules.ts index c98cd401..f3daec9a 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -20,6 +20,7 @@ import { wLogWithWarn, } from "../utils/log" import camelize from "../utils/camelize" +import has from "lodash.has" /** * Wertik js module @@ -187,6 +188,14 @@ export const withModule = (moduleProps: WithModuleProps) => { let updateSchema = [] if (useDatabase) { + if (!has(app.database, moduleProps.database)) { + wLogWithError( + `Unknown database: ${moduleProps.database}`, + `Unknown database mentioned in module ${moduleProps.name}` + ) + process.exit() + } + const connection = app.database[moduleProps.database] // info const tableInfo = await getMysqlTableInfo( diff --git a/src/store.ts b/src/store.ts index e520906a..6907a660 100644 --- a/src/store.ts +++ b/src/store.ts @@ -2,13 +2,12 @@ import generalSchema from "./graphql/generalSchema" import { WertikApp } from "./types" import { WithModuleProps } from "./types/modules" - type StoreDatabaseRelationship = { - currentModule: string, - currentModuleDatabase: string, - graphqlKey: string, - referencedModule: string, - referencedModuleDatabase: string, + currentModule: string + currentModuleDatabase: string + graphqlKey: string + referencedModule: string + referencedModuleDatabase: string options: { [key: string]: unknown } @@ -60,7 +59,7 @@ const store: { models: { [key: string]: any } - }, + } modules: WithModuleProps[] } = { graphql: { @@ -95,7 +94,7 @@ const store: { relationships: [], models: {}, }, - modules: [] + modules: [], } export default store diff --git a/src/types/graphql.ts b/src/types/graphql.ts index 7b4a9a04..3c677d7d 100644 --- a/src/types/graphql.ts +++ b/src/types/graphql.ts @@ -16,6 +16,9 @@ export interface WithApolloGraphqlProps { } typeDefs?: string storeTypeDefFilePath?: string + validation?: { + depthLimit?: number + } } export interface GraphqlInitializeProps { diff --git a/src/types/index.ts b/src/types/index.ts index 85bb2949..497b4a34 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -21,7 +21,7 @@ export interface Store { } database: { relationships: Array - }, + } modules: WithModuleProps[] } diff --git a/src/types/modules.ts b/src/types/modules.ts index dadc3985..f80d1671 100644 --- a/src/types/modules.ts +++ b/src/types/modules.ts @@ -182,6 +182,6 @@ export interface WertikModule { update: string | any[] list: string filters: string - }, + } props: WithModuleProps } diff --git a/src/utils/log.ts b/src/utils/log.ts index c0761cd0..f3d064de 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -26,4 +26,4 @@ export const wLogWithSuccess = (info, ...params) => { export const wLogWithWarn = (info, ...params) => { console.log(chalk.bgYellow.bold("WARN"), info, ...params) -} \ No newline at end of file +} diff --git a/test_database.sql b/test_database.sql index 890162f1..1b054784 100644 --- a/test_database.sql +++ b/test_database.sql @@ -1,48 +1,56 @@ --- Create database wertik if not exists -CREATE DATABASE IF NOT EXISTS `wertik` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */; +-- ------------------------------------------------------------- +-- TablePlus 5.9.6(546) +-- +-- https://tableplus.com/ +-- +-- Database: wertik +-- Generation Time: 2024-04-13 14:45:19.0820 +-- ------------------------------------------------------------- + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -USE `wertik`; -DROP TABLE IF EXISTS `product`; -DROP TABLE IF EXISTS `user`; DROP TABLE IF EXISTS `category`; - -CREATE TABLE `user` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `name` varchar(10) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, +CREATE TABLE `category` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=latin1; +DROP TABLE IF EXISTS `product`; CREATE TABLE `product` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, + `id` bigint NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `sizes` enum('lg','sm','xl','xxl','xxxl') DEFAULT NULL, - `user_id` bigint(20), - `category_id` bigint(20), + `user_id` bigint DEFAULT NULL, + `category_id` bigint DEFAULT NULL, PRIMARY KEY (`id`), - FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL, - FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE SET NULL -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; + KEY `user_id` (`user_id`), + KEY `category_id` (`category_id`), + CONSTRAINT `product_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL, + CONSTRAINT `product_ibfk_2` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; --- add table category -CREATE TABLE `category` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `title` varchar(255) NOT NULL, +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `name` varchar(10) DEFAULT NULL, + `email` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; - --- insert two users -INSERT INTO `user` (`id`, `name`, `email`) VALUES -(1, 'John', 'asdfasdf@gmail.com'), -(2, 'ali', 'ali@gmail.com'); - --- insert two categories -INSERT INTO `category` (`id`, `title`) VALUES -(1, 'T-shirt'), -(2, 'Pants'); - --- insert two products -INSERT INTO `product` (`id`, `title`, `sizes`, `user_id`, `category_id`) VALUES -(1, 'T-shirt 1', 'lg', 1, 1), -(2, 'T-shirt 2', 'sm', 1, 2); +) ENGINE=InnoDB AUTO_INCREMENT=118 DEFAULT CHARSET=latin1; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; \ No newline at end of file diff --git a/tests/graphql.test.js b/tests/graphql.test.js index fa515ba6..98cde26a 100644 --- a/tests/graphql.test.js +++ b/tests/graphql.test.js @@ -38,9 +38,9 @@ if (database.name) { mutation { insert_products(input: { sizes: lg - user_id: 1 + user_id: 120 title: "My first product" - category_id: 1 + category_id: 50 }) { returning { id @@ -68,9 +68,7 @@ if (database.name) { } `, }) - expect(updatedItem.data.update_products.returning[0].id).toBeGreaterThan( - 0 - ) + expect(updatedItem.data.update_products.returning[0].id).toBeGreaterThan(0) expect(updatedItem.data.update_products.returning[0].sizes).toBe("xxxl") }) // view @@ -88,7 +86,7 @@ if (database.name) { }) expect(viewItem.data.product.sizes).toBe("xxxl") }) - // deleted + // delete test("Expect graphql to delete data", async () => { let deletedItem = await app.graphql.executeOperation({ query: ` @@ -125,7 +123,7 @@ if (database.name) { let viewItem = await app.graphql.executeOperation({ query: ` query { - user(where: { id: { _eq: 1 } }) { + user(where: { id: { _eq: 122 } }) { id name products { diff --git a/wertik-test-database/category.json b/wertik-test-database/category.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/wertik-test-database/category.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/wertik-test-database/product.json b/wertik-test-database/product.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/wertik-test-database/product.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/wertik-test-database/user.json b/wertik-test-database/user.json new file mode 100644 index 00000000..c23a7ef3 --- /dev/null +++ b/wertik-test-database/user.json @@ -0,0 +1,522 @@ +[ + { + "id": 14, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 15, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 16, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 17, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 18, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 19, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 20, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 21, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 22, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 23, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 24, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 25, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 26, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 27, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 28, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 29, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 30, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 31, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 32, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 33, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 34, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 35, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 36, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 37, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 38, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 39, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 40, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 41, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 42, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 43, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 44, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 45, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 46, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 47, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 48, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 49, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 50, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 51, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 52, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 53, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 54, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 55, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 56, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 57, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 58, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 59, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 60, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 61, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 62, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 63, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 64, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 65, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 66, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 67, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 68, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 69, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 70, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 71, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 72, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 73, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 74, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 75, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 76, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 77, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 78, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 79, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 80, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 81, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 82, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 83, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 84, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 85, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 86, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 87, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 88, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 89, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 90, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 91, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 92, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 93, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 94, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 95, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 96, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 97, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 98, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 99, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 100, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 101, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 102, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 103, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 104, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 105, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 106, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 107, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 108, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 109, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 110, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 111, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 112, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 113, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 114, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 115, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 116, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + }, + { + "id": 117, + "name": "Ali", + "email": "ilyas.datoo@gmail.com" + } +] \ No newline at end of file diff --git a/wertik.sql b/wertik.sql new file mode 100644 index 00000000..1b054784 --- /dev/null +++ b/wertik.sql @@ -0,0 +1,56 @@ +-- ------------------------------------------------------------- +-- TablePlus 5.9.6(546) +-- +-- https://tableplus.com/ +-- +-- Database: wertik +-- Generation Time: 2024-04-13 14:45:19.0820 +-- ------------------------------------------------------------- + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + + +DROP TABLE IF EXISTS `category`; +CREATE TABLE `category` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=latin1; + +DROP TABLE IF EXISTS `product`; +CREATE TABLE `product` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, + `sizes` enum('lg','sm','xl','xxl','xxxl') DEFAULT NULL, + `user_id` bigint DEFAULT NULL, + `category_id` bigint DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `category_id` (`category_id`), + CONSTRAINT `product_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL, + CONSTRAINT `product_ibfk_2` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `name` varchar(10) DEFAULT NULL, + `email` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=118 DEFAULT CHARSET=latin1; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; \ No newline at end of file From e9563b1f3f8c479a292e58c9e29491f7fb4df56c Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sat, 13 Apr 2024 15:01:52 +0500 Subject: [PATCH 48/52] Remove test-database-folder --- wertik-test-database/category.json | 3 - wertik-test-database/product.json | 3 - wertik-test-database/user.json | 522 ----------------------------- 3 files changed, 528 deletions(-) delete mode 100644 wertik-test-database/category.json delete mode 100644 wertik-test-database/product.json delete mode 100644 wertik-test-database/user.json diff --git a/wertik-test-database/category.json b/wertik-test-database/category.json deleted file mode 100644 index c44dc44f..00000000 --- a/wertik-test-database/category.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - -] \ No newline at end of file diff --git a/wertik-test-database/product.json b/wertik-test-database/product.json deleted file mode 100644 index c44dc44f..00000000 --- a/wertik-test-database/product.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - -] \ No newline at end of file diff --git a/wertik-test-database/user.json b/wertik-test-database/user.json deleted file mode 100644 index c23a7ef3..00000000 --- a/wertik-test-database/user.json +++ /dev/null @@ -1,522 +0,0 @@ -[ - { - "id": 14, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 15, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 16, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 17, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 18, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 19, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 20, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 21, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 22, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 23, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 24, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 25, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 26, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 27, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 28, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 29, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 30, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 31, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 32, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 33, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 34, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 35, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 36, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 37, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 38, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 39, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 40, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 41, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 42, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 43, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 44, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 45, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 46, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 47, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 48, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 49, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 50, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 51, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 52, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 53, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 54, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 55, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 56, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 57, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 58, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 59, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 60, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 61, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 62, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 63, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 64, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 65, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 66, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 67, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 68, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 69, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 70, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 71, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 72, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 73, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 74, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 75, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 76, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 77, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 78, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 79, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 80, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 81, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 82, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 83, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 84, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 85, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 86, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 87, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 88, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 89, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 90, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 91, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 92, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 93, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 94, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 95, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 96, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 97, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 98, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 99, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 100, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 101, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 102, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 103, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 104, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 105, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 106, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 107, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 108, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 109, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 110, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 111, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 112, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 113, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 114, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 115, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 116, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - }, - { - "id": 117, - "name": "Ali", - "email": "ilyas.datoo@gmail.com" - } -] \ No newline at end of file From 8d77d53d4d373d19f4a38d2f56d38d54c1b5c252 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sun, 14 Apr 2024 13:30:54 +0500 Subject: [PATCH 49/52] Tables in database initial work --- changelog.md | 3 +- src/crud/index.ts | 10 +-- src/database/mysql/getTableInfo.ts | 10 +-- src/database/mysql/mysql.ts | 39 +++++++++- src/devServer.ts | 111 ++++++++++++++++++----------- src/devServerTestModules.ts | 78 ++++++++++++++++++++ src/modules/modules.ts | 20 +++--- src/modules/modulesHelpers.ts | 10 +-- src/types/database.ts | 70 +++++++++++++++++- src/types/index.ts | 5 +- 10 files changed, 282 insertions(+), 74 deletions(-) create mode 100644 src/devServerTestModules.ts diff --git a/changelog.md b/changelog.md index 132f30af..0c279608 100644 --- a/changelog.md +++ b/changelog.md @@ -22,7 +22,6 @@ - Rename `useMysqlDatabase` to `withMysqlDatabase` with its props. - Rename `useRedis` to `withRedis` with its props. - Rename `useGraphql` to `withGraphql` with its props. -- Rename `useModule` to `withModule` with its props. - Rename `useMailer` to `withMailer` with its props. - Rename `useSocketIO` to `withSocketIO` with its props. - Rename `useWebSockets` to `withWebSockets` with its props. @@ -34,7 +33,7 @@ - Rename `useSchema` to `extendSchema` with its props. - Rename `useMutation` to `addMutation` with its props. - Rename `useExpress` to `getExpress`. - +- BREAKING CHANGE: Remove `useModule` and allowed using tables on database configuration. ### 3.3.0 diff --git a/src/crud/index.ts b/src/crud/index.ts index 90365d92..03024084 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -3,8 +3,8 @@ import { wLogWithDateWithInfo } from "../utils/log" import { convertGraphqlRequestedFieldsIntoInclude } from "../database/eagerLoadingGraphqlQuery" import { generateRequestedFieldsFromGraphqlInfo, - generateRowFieldNameForModuleName, - generateRowsFieldNameForModuleName, + convertWordIntoSingular, + convertWordIntoPlural, } from "../modules/modulesHelpers" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import graphqlFields from "graphql-fields" @@ -13,8 +13,8 @@ import omit from "lodash.omit" import { voidFunction } from "../utils/voidFunction" export default function (module, schemaInformation, store) { - let rowsFieldName = generateRowsFieldNameForModuleName(module.name) - let singleRowFieldName = generateRowFieldNameForModuleName(module.name) + let rowsFieldName = convertWordIntoPlural(module.name) + let singleRowFieldName = convertWordIntoSingular(module.name) return { graphql: { @@ -37,7 +37,7 @@ export default function (module, schemaInformation, store) { ${singleRowFieldName}(where: ${singleRowFieldName}_filter_input): ${ module.name }Module - ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, order: ${generateRowFieldNameForModuleName( + ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, order: ${convertWordIntoSingular( module.name )}_order_input): ${module.name}List count${module.name}(where: ${singleRowFieldName}_filter_input): Int diff --git a/src/database/mysql/getTableInfo.ts b/src/database/mysql/getTableInfo.ts index 3fc1a10a..e6638d2f 100644 --- a/src/database/mysql/getTableInfo.ts +++ b/src/database/mysql/getTableInfo.ts @@ -30,10 +30,10 @@ export const enumTypes = ["enum"] export const jsonTypes = ["json"] export const getMysqlTableInfo = async ( - module: WithModuleProps, + tableName: string, sequelize: any ): Promise => { - let rows = await sequelize.query(`describe ${module.table};`) + let rows = await sequelize.query(`describe ${tableName};`) rows = rows[0] if (rows) { @@ -42,7 +42,7 @@ export const getMysqlTableInfo = async ( ).map((element) => { const graphqlType = convertDatabaseTypeIntoGraphqlType( element, - module.name + tableName ) let isPrimary = element.Key === "PRI" const isNull = element.Null === "YES" @@ -63,14 +63,14 @@ export const getMysqlTableInfo = async ( }) return { - name: module.table, + name: tableName, columns: fields, originalDescribeColumns: rows, } } return { - name: module.table, + name: tableName, columns: [], originalDescribeColumns: rows, } diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 12dae3a1..698c8a99 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -1,4 +1,4 @@ -import { Sequelize } from "sequelize" +import { Model, ModelAttributes, ModelCtor, Sequelize } from "sequelize" import { databaseDefaultOptions } from "../../utils/defaultOptions" import { WithMysqlDatabaseProps } from "../../types/database" import get from "lodash.get" @@ -8,6 +8,7 @@ import { wLogWithInfo, wLogWithSuccess, } from "../../utils/log" +import { getMysqlTableInfo } from "./getTableInfo" export const getAllRelationships = (dbName: string) => { return ` @@ -37,6 +38,41 @@ export const withMysqlDatabase = function (obj: WithMysqlDatabaseProps) { wLog(error) process.exit(1) }) + let models: ModelCtor>[] = [] + obj.tables?.forEach(async table => { + const tableInfo = await getMysqlTableInfo( + table.name, + sequelize + ) + + let fields: ModelAttributes, any> = {} + + tableInfo.columns.forEach((column) => { + if (column.columnName === "id") return + fields[column.columnName] = { + type: column.databaseType, + allowNull: column.isNull, + defaultValue: column.default, + primaryKey: column.isPrimary, + values: column.isEnum ? column.enumValues : null, + } + }) + + const tableInstance = sequelize.define( + table.name, + { + ...fields, + ...get(table, "extendFields", {}), + }, + { + ...get(table, "tableOptions", {}), + ...databaseDefaultOptions.sql.defaultTableOptions, + } + ) + models.push(tableInstance) + }); + + wLogWithSuccess( `[Wertik-Mysql-Database]`, `Successfully connected to database ${obj.name}` @@ -47,6 +83,7 @@ export const withMysqlDatabase = function (obj: WithMysqlDatabaseProps) { return { credentials: obj, instance: sequelize, + models: models, } } catch (e) { wLog(`[DB] Connecting failed to database ${obj.name}`) diff --git a/src/devServer.ts b/src/devServer.ts index 413003ce..eb8b1d9d 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -10,6 +10,7 @@ import wertik, { withMailer, withRedis, } from "./index" +import modules from "./devServerTestModules" wertik({ port: 1200, @@ -23,52 +24,76 @@ wertik({ host: "127.0.0.1", password: "pass", username: "root", + tables: [ + { + name: "user", + relationships: { + hasMany: { + products: { + as: "products", + foreignKey: "user_id", + sourceKey: "id", + } + } + } + }, + { + name: "product", + }, + ], }), - }, - modules: { - Product: withModule({ - name: "Product", - useDatabase: true, - database: "wertik", - table: "product", - on: function ({ belongsTo }) { - belongsTo({ - database: "wertik", - graphqlKey: "user", - module: "User", - options: { - as: "user", - foreignKey: "user_id", - targetKey: "id", - }, - }) - }, - }), - User: withModule({ - name: "User", - useDatabase: true, - database: "wertik", - table: "user", - on: function ({ hasMany }) { - hasMany({ - database: "wertik", - graphqlKey: "products", - module: "Product", - options: { - as: "products", - foreignKey: "user_id", - sourceKey: "id", - }, - }) - }, - }), - Category: withModule({ - name: "Category", - useDatabase: true, - database: "wertik", - table: "category", + default: withMysqlDatabase({ + username: "root", + password: "pass", + name: "wapgee_prod", + host: "localhost", + port: 3306, }), }, + // modules: modules, + // Product: withModule({ + // name: "Product", + // useDatabase: true, + // database: "wertik", + // table: "product", + // on: function ({ belongsTo }) { + // belongsTo({ + // database: "wertik", + // graphqlKey: "user", + // module: "User", + // options: { + // as: "user", + // foreignKey: "user_id", + // targetKey: "id", + // }, + // }) + // }, + // }), + // User: withModule({ + // name: "User", + // useDatabase: true, + // database: "wertik", + // table: "user", + // on: function ({ hasMany }) { + // hasMany({ + // database: "wertik", + // graphqlKey: "products", + // module: "Product", + // options: { + // as: "products", + // foreignKey: "user_id", + // sourceKey: "id", + // }, + // }) + // }, + // }), + // Category: withModule({ + // name: "Category", + // useDatabase: true, + // database: "wertik", + // table: "category", + // }), + // }, sockets: { mySockets: withWebSockets({ path: "/websockets", diff --git a/src/devServerTestModules.ts b/src/devServerTestModules.ts new file mode 100644 index 00000000..2dc818b1 --- /dev/null +++ b/src/devServerTestModules.ts @@ -0,0 +1,78 @@ +import wertik, { withModule } from "./index" + +export default { + User: withModule({ + name: "User", + useDatabase: true, + database: "default", + table: "users", + on: function ({ hasOne, hasMany, belongsTo, getExpress }) { + hasMany({ + database: "default", + module: "Post", + graphqlKey: "posts", + options: { + as: "posts", + foreignKey: "created_by", + sourceKey: "id", + }, + }) + }, + }), + Comment: withModule({ + name: "Comment", + useDatabase: true, + database: "default", + table: "comments", + on: function ({ hasOne }) { + hasOne({ + module: "Post", + graphqlKey: "post", + database: "default", + options: { + sourceKey: "post_id", + foreignKey: "id", + as: "post", + }, + }) + hasOne({ + module: "User", + graphqlKey: "created_by", + database: "default", + options: { + sourceKey: "created_by_id", + foreignKey: "id", + as: "created_by", + }, + }) + }, + }), + Post: withModule({ + name: "Post", + useDatabase: true, + database: "default", + table: "post", + on: function ({ hasOne, hasMany, belongsTo, getExpress }) { + hasOne({ + module: "User", + graphqlKey: "author", + database: "default", + options: { + as: "author", + sourceKey: "created_by", + foreignKey: "id", + }, + }) + hasOne({ + module: "User", + graphqlKey: "last_updated_by", + database: "default", + options: { + as: "last_updated_by", + sourceKey: "last_updated_by_id", + foreignKey: "id", + }, + }) + }, + }), +} diff --git a/src/modules/modules.ts b/src/modules/modules.ts index f3daec9a..ad8f5f72 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -6,8 +6,8 @@ import { getUpdateSchema, generateEnumTypeForGraphql, generateGenerateGraphQLCrud, - generateRowsFieldNameForModuleName, - generateRowFieldNameForModuleName, + convertWordIntoPlural, + convertWordIntoSingular, getOrderSchema, } from "./modulesHelpers" import { getMysqlTableInfo } from "../database/mysql/getTableInfo" @@ -42,7 +42,7 @@ export const withModule = (moduleProps: WithModuleProps) => { let graphqlSchema = [`type ${moduleProps.name}Module {`] let listSchema = "" let filterSchema = [ - `input ${generateRowFieldNameForModuleName( + `input ${convertWordIntoSingular( moduleProps.name )}_filter_input {`, ] @@ -102,7 +102,7 @@ export const withModule = (moduleProps: WithModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName( + `${camelize(params.graphqlKey)}: ${convertWordIntoSingular( params.module )}_filter_input` ) @@ -122,13 +122,13 @@ export const withModule = (moduleProps: WithModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName( + `${camelize(params.graphqlKey)}: ${convertWordIntoSingular( params.module )}_filter_input` ) } const belongsToMany = (params: RelationParams) => { - let field_name = generateRowFieldNameForModuleName(params.module) + let field_name = convertWordIntoSingular(params.module) graphqlSchema.push( `${params.graphqlKey}(offset: Int, limit: Int, where: ${field_name}_filter_input, order: ${field_name}_order_input): [${params.module}Module]` ) @@ -145,13 +145,13 @@ export const withModule = (moduleProps: WithModuleProps) => { currentModuleRelationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName( + `${camelize(params.graphqlKey)}: ${convertWordIntoSingular( params.module )}_filter_input` ) } const hasMany = (params: RelationParams) => { - let field_name = generateRowFieldNameForModuleName(params.module) + let field_name = convertWordIntoSingular(params.module) graphqlSchema.push( `${params.graphqlKey}(offset: Int, limit: Int, where: ${field_name}_filter_input, order: ${field_name}_order_input): [${params.module}Module]` ) @@ -168,7 +168,7 @@ export const withModule = (moduleProps: WithModuleProps) => { store.database.relationships.push(relationshipInfo) store.graphql.graphqlKeys.push(camelize(params.module)) filterSchema.push( - `${camelize(params.graphqlKey)}: ${generateRowFieldNameForModuleName( + `${camelize(params.graphqlKey)}: ${convertWordIntoSingular( params.module )}_filter_input` ) @@ -199,7 +199,7 @@ export const withModule = (moduleProps: WithModuleProps) => { const connection = app.database[moduleProps.database] // info const tableInfo = await getMysqlTableInfo( - moduleProps, + moduleProps.table, connection.instance ) diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index e6e3b8d2..62de1f59 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -88,7 +88,7 @@ export const getInsertSchema = ( tableInfo: TableInfo ) => { const optionsInsertSchema = get(module, "graphql.createSchema", "") - const rowsFieldName = generateRowsFieldNameForModuleName(module.name) + const rowsFieldName = convertWordIntoPlural(module.name) if (optionsInsertSchema) return optionsInsertSchema let insertSchema = [`input insert_${rowsFieldName}_input {`] tableInfo.columns.forEach((column) => { @@ -105,7 +105,7 @@ export const getInsertSchema = ( export const getOrderSchema = (module: WithModuleProps, tableInfo) => { let orderSchema = [ - `input ${generateRowFieldNameForModuleName(module.name)}_order_input {`, + `input ${convertWordIntoSingular(module.name)}_order_input {`, ] let relationships = store.database.relationships.filter( (c) => c.currentModule === module.name @@ -186,12 +186,12 @@ export const generateRequestedFieldsFromGraphqlInfo = (info) => { return Object.keys(info).filter((c) => !keys.includes(c)) } -export const generateRowFieldNameForModuleName = (moduleName) => { +export const convertWordIntoSingular = (moduleName) => { return snackCase(pluralize.singular(moduleName)).toLowerCase() } -export const generateRowsFieldNameForModuleName = (moduleName) => { +export const convertWordIntoPlural = (moduleName) => { let fieldName = snackCase(pluralize.plural(moduleName)).toLowerCase() - if (generateRowFieldNameForModuleName(moduleName) == fieldName) { + if (convertWordIntoSingular(moduleName) == fieldName) { return fieldName + "s" } return fieldName diff --git a/src/types/database.ts b/src/types/database.ts index 039052c5..acea9a36 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -1,5 +1,69 @@ import { iObject } from "." +import { AddMutationProps, AddQueryProps, RelationParams } from "./modules" +export interface SqlTable { + name: string, + /** + * Sequelize Table Options + */ + tableOptions?: iObject + /** + * Provide set of fields to extend a table, mostly can be used to update createdAt and updatedAt columns. + */ + extendFields?: iObject + /** + * Graphql options for this module. + */ + graphql?: { + /** + * Wertik-js creates schema by default from the database table. Once you defined this Wertik-js will ignore taking schema from the database. + */ + schema?: string + /** + * Wertik-js creates an update schema from the database table. Once defined, Wertik JS will ignore creating an update schema from table information. + */ + updateSchema?: string + /** + * Wertik-js creates create a schema from the database table. Once defined this, Wertik JS will ignore creating create a schema from the table information. + */ + insertSchema: string + }, + relationships?: { + hasOne?: { + [tableName: string]: { + as: string, + foreignKey: string, + targetKey: string, + [key: string]: any + } + }, + hasMany?: { + [tableName: string]: { + as: string, + foreignKey: string, + sourceKey: string, + [key: string]: any + } + }, + belongsTo?: { + [tableName: string]: { + as: string, + foreignKey: string, + targetKey: string, + [key: string]: any + } + }, + belongsToMany?: { + [tableName: string]: { + as: string, + through: string, + foreignKey: string, + otherKey: string, + [key: string]: any + } + } + } +} export interface WithMysqlDatabaseProps { /** * Database name @@ -24,7 +88,11 @@ export interface WithMysqlDatabaseProps { /** * Sequelize Database options. */ - options?: iObject + options?: iObject, + /** + * Tables + */ + tables?: SqlTable[]; } export interface TableInfo { diff --git a/src/types/index.ts b/src/types/index.ts index 497b4a34..9e08fd53 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -62,11 +62,12 @@ export interface WertikConfiguration { database?: { [key: string]: () => Promise<{ credentials: WithMysqlDatabaseProps - instance: Sequelize + instance: Sequelize, }> } /** * Modules + * @deprecated Use `tables` on database connections. */ modules?: { [key: string]: (options: { @@ -197,7 +198,7 @@ export interface WertikApp { username: string host: string } - instance: Sequelize + instance: Sequelize, } } models: { From 9425106c9604407eb257ada8a3025851e1a4de2f Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sun, 14 Apr 2024 16:39:33 +0500 Subject: [PATCH 50/52] Add initial schema from tables in database and change location of store into wertikApp.store --- src/crud/index.ts | 70 +++++++++--------- src/crud/paginate.ts | 6 +- src/database/database.ts | 28 -------- src/database/eagerLoadingGraphqlQuery.ts | 8 +-- src/database/helpers.ts | 13 ++++ src/database/mysql/mysql.ts | 71 ++++++++++++++++-- src/graphql/index.ts | 24 ++++--- src/index.ts | 15 ++-- src/modules/modules.ts | 4 +- src/modules/modulesHelpers.ts | 49 +++++++------ src/store.ts | 92 +++++++++--------------- src/types/index.ts | 28 +++++++- 12 files changed, 221 insertions(+), 187 deletions(-) delete mode 100644 src/database/database.ts diff --git a/src/crud/index.ts b/src/crud/index.ts index 03024084..0f95d37d 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -12,44 +12,40 @@ import { paginate } from "./paginate" import omit from "lodash.omit" import { voidFunction } from "../utils/voidFunction" -export default function (module, schemaInformation, store) { - let rowsFieldName = convertWordIntoPlural(module.name) - let singleRowFieldName = convertWordIntoSingular(module.name) +export default function (table, schemaInformation, store) { + let rowsFieldName = convertWordIntoPlural(table.name) + let singleRowFieldName = convertWordIntoSingular(table.name) return { graphql: { generateQueriesCrudSchema() { return ` - type ${module.name}List { - rows: [${module.name}Module] + type ${table.name}List { + rows: [${table.name}] pagination: Pagination paginationProperties: PaginationProperties @deprecated(reason: "Use pagination instead") } - type ${module.name}BulkMutationResponse { - returning: [${module.name}Module] + type ${table.name}_bulk_mutation_response { + returning: [${table.name}] affectedRows: Int } - type Count${module.name} { + type Count${table.name} { count: Int } extend type Query { - ${singleRowFieldName}(where: ${singleRowFieldName}_filter_input): ${ - module.name - }Module - ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, order: ${convertWordIntoSingular( - module.name - )}_order_input): ${module.name}List - count${module.name}(where: ${singleRowFieldName}_filter_input): Int + ${singleRowFieldName}(where: ${singleRowFieldName}_filter_input): ${table.name} + ${rowsFieldName}(pagination: PaginationInput, where: ${singleRowFieldName}_filter_input, order: ${convertWordIntoSingular(table.name)}_order_input): ${table.name}List + count${table.name}(where: ${singleRowFieldName}_filter_input): Int }` }, generateMutationsCrudSchema() { return ` extend type Mutation { - update_${rowsFieldName}(input: update${module.name}Input,where: ${singleRowFieldName}_filter_input!): ${module.name}BulkMutationResponse - insert_${rowsFieldName}(input: [insert_${rowsFieldName}_input]): ${module.name}BulkMutationResponse + update_${rowsFieldName}(input: update_${table.name}_input,where: ${singleRowFieldName}_filter_input!): ${table.name}_bulk_mutation_response + insert_${rowsFieldName}(input: [insert_${rowsFieldName}_input]): ${table.name}_bulk_mutation_response delete_${rowsFieldName}(where: ${singleRowFieldName}_filter_input!): SuccessResponse - insert_or_update_${rowsFieldName}(id: Int, input: insert_${rowsFieldName}_input): ${module.name}List + insert_or_update_${rowsFieldName}(id: Int, input: insert_${rowsFieldName}_input): ${table.name}List } ` }, @@ -57,7 +53,7 @@ export default function (module, schemaInformation, store) { return { Mutation: { [`insert_or_update_${rowsFieldName}`]: get( - module, + table, "graphql.mutations.InsertOrUpdate", async (_, args, context, info) => { wLogWithDateWithInfo( @@ -65,7 +61,7 @@ export default function (module, schemaInformation, store) { `${info.fieldName} - ${JSON.stringify(args)}` ) const argsFromEvent = await get( - module, + table, "events.beforeInsertOrUpdate", voidFunction )(_, args, context, info) @@ -80,7 +76,7 @@ export default function (module, schemaInformation, store) { }) if (!___find) { - throw new Error(`${module.name} Not found`) + throw new Error(`${table.name} Not found`) } await schemaInformation.tableInstance.update(args.input, { @@ -98,7 +94,7 @@ export default function (module, schemaInformation, store) { } ), [`update_${rowsFieldName}`]: get( - module, + table, "graphql.mutations.update", async (_, args, context, info) => { wLogWithDateWithInfo( @@ -106,7 +102,7 @@ export default function (module, schemaInformation, store) { `${info.fieldName} - ${JSON.stringify(args)}` ) const argsFromEvent = await get( - module, + table, "events.beforeUpdate", voidFunction )(_, args, context, info) @@ -130,7 +126,7 @@ export default function (module, schemaInformation, store) { } ), [`delete_${rowsFieldName}`]: get( - module, + table, "graphql.mutations.delete", async (_, args, context, info) => { wLogWithDateWithInfo( @@ -138,7 +134,7 @@ export default function (module, schemaInformation, store) { `${info.fieldName} - ${JSON.stringify(args)}` ) const argsFromEvent = await get( - module, + table, "events.beforeDelete", voidFunction )(_, args, context, info) @@ -149,11 +145,11 @@ export default function (module, schemaInformation, store) { await schemaInformation.tableInstance.destroy({ where: where, }) - return { message: `${module.name} Deleted` } + return { message: `${table.name} Deleted` } } ), [`insert_${rowsFieldName}`]: get( - module, + table, "graphql.mutations.create", async (_, args, context, info) => { wLogWithDateWithInfo( @@ -161,7 +157,7 @@ export default function (module, schemaInformation, store) { `${info.fieldName} - ${JSON.stringify(args)}` ) const argsFromEvent = await get( - module, + table, "events.beforeCreate", voidFunction )(_, args, context, info) @@ -181,7 +177,7 @@ export default function (module, schemaInformation, store) { }, Query: { [singleRowFieldName]: get( - module, + table, "graphql.queries.view", async (_, args, context, info) => { wLogWithDateWithInfo( @@ -189,7 +185,7 @@ export default function (module, schemaInformation, store) { `${info.fieldName} - ${JSON.stringify(args)}` ) const argsFromEvent = await get( - module, + table, "events.beforeView", voidFunction )(_, args, context, info) @@ -207,7 +203,7 @@ export default function (module, schemaInformation, store) { convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }), args, - module + table ) const find = await schemaInformation.tableInstance.findOne({ @@ -223,7 +219,7 @@ export default function (module, schemaInformation, store) { } ), [rowsFieldName]: get( - module, + table, "graphql.queries.list", async (_, args, context, info) => { wLogWithDateWithInfo( @@ -231,7 +227,7 @@ export default function (module, schemaInformation, store) { `${rowsFieldName} - args ${JSON.stringify(args)}` ) const argsFromEvent = await get( - module, + table, "events.beforeList", voidFunction )(_, args, context, info) @@ -241,7 +237,7 @@ export default function (module, schemaInformation, store) { convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }), args, - module + table ) return await paginate( @@ -257,8 +253,8 @@ export default function (module, schemaInformation, store) { ) } ), - [`count${module.name}`]: get( - module, + [`count${table.name}`]: get( + table, "graphql.queries.count", async (_, args, context, info) => { wLogWithDateWithInfo( @@ -266,7 +262,7 @@ export default function (module, schemaInformation, store) { `${info.fieldName} - ${JSON.stringify(args)}` ) const argsFromEvent = await get( - module, + table, "events.beforeCount", voidFunction )(_, args, context, info) diff --git a/src/crud/paginate.ts b/src/crud/paginate.ts index 00d21410..c622c431 100644 --- a/src/crud/paginate.ts +++ b/src/crud/paginate.ts @@ -1,7 +1,7 @@ -import store from "../store" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import omit from "lodash.omit" import isPlainObject from "lodash.isplainobject" +import { wertikApp } from "../store" export const paginate = async ( arg, @@ -13,8 +13,8 @@ export const paginate = async ( const { page = 1, limit = 100 } = arg.pagination ?? {} const offset = limit * (page - 1) const keys = [ - ...store.database.relationships.map((c) => c.graphqlKey), - ...store.graphql.graphqlKeys, + ...wertikApp.store.database.relationships.map((c) => c.graphqlKey), + ...wertikApp.store.graphql.graphqlKeys, ] let where = omit(convertFiltersIntoSequelizeObject(arg.where), keys) diff --git a/src/database/database.ts b/src/database/database.ts deleted file mode 100644 index f3f58573..00000000 --- a/src/database/database.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { wLog, wLogWithInfo } from "../utils/log" -import { Store, WertikApp } from "../types" -import logSymbols from "log-symbols" - -export const applyRelationshipsFromStoreToDatabase = async ( - store: Store, - app: WertikApp -) => { - if (store.database.relationships.length > 0) - wLogWithInfo("[Wertik-Database]", "Registering relationships \n") - store.database.relationships.forEach((element) => { - const currentTable = app.modules[element.currentModule].tableInstance - const referencedTable = app.modules[element.referencedModule].tableInstance - const currentTableName = currentTable.getTableName() as string - const referencedTableName = referencedTable.getTableName() as string - - wLog( - logSymbols.success, - `${currentTableName}.${ - element.type - }(${referencedTableName}, ${JSON.stringify(element.options)})` - ) - - // element.type will be hasOne, hasMany, belongsTo or belongsToMany - currentTable[element.type](referencedTable, element.options || {}) - }) - if (store.database.relationships.length > 0) wLog("\n") -} diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index faa4b6e4..de180333 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -1,4 +1,4 @@ -import store, { wertikApp } from "../store" +import { wertikApp } from "../store" import isPlainObject from "lodash.isplainobject" import get from "lodash.get" import has from "lodash.has" @@ -34,14 +34,14 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( let order = [] let depth = [] graphqlFields = clean(graphqlFields) - const currentModuleRelationships = store.database.relationships.filter( + const currentModuleRelationships = wertikApp.store.database.relationships.filter( (f) => f.currentModule == module.name ) const currentModuleRelationshipsKeys = currentModuleRelationships.map( (c) => c.graphqlKey ) - const allRelationshipKeys = store.database.relationships.map( + const allRelationshipKeys = wertikApp.store.database.relationships.map( (c) => c.graphqlKey ) @@ -60,7 +60,7 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( if (allRelationshipKeys.includes(key)) { depth.push(key) let _localDepth = [...JSON.parse(JSON.stringify(depth))] - const relationship = store.database.relationships.find( + const relationship = wertikApp.store.database.relationships.find( (c) => c.graphqlKey === key ) const sequelizeModel = wertikApp.models[relationship.referencedModule] diff --git a/src/database/helpers.ts b/src/database/helpers.ts index 9cfb3e56..d2f51000 100644 --- a/src/database/helpers.ts +++ b/src/database/helpers.ts @@ -7,6 +7,8 @@ import { enumTypes, jsonTypes, } from "./mysql/getTableInfo" +import { WertikApp } from "src/types" +import get from "lodash.get" export const convertDatabaseTypeIntoGraphqlType = ( columnInfo: MysqlColumnInfoDescribeTable, @@ -73,3 +75,14 @@ export const convertDatabaseTypeIntoGraphqlType = ( } } } + + + +export const applyRelationshipsFromStoreToDatabase = async ( + app: WertikApp +) => { + Object.keys(app.database).forEach(dbName => { + let db = app.database[dbName] + console.log(db.credentials) + }); +} diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 698c8a99..47ede29d 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -9,6 +9,15 @@ import { wLogWithSuccess, } from "../../utils/log" import { getMysqlTableInfo } from "./getTableInfo" +import { + convertWordIntoSingular, + generateEnumTypeForGraphql, + generateGenerateGraphQLCrud, + getInsertSchema, + getOrderSchema, + getUpdateSchema, +} from "../../modules/modulesHelpers" +import { wertikApp } from "../../store" export const getAllRelationships = (dbName: string) => { return ` @@ -39,16 +48,45 @@ export const withMysqlDatabase = function (obj: WithMysqlDatabaseProps) { process.exit(1) }) let models: ModelCtor>[] = [] - obj.tables?.forEach(async table => { - const tableInfo = await getMysqlTableInfo( - table.name, - sequelize - ) + obj.tables?.forEach(async (table) => { + + let graphqlSchema = [`type ${table.name} {`] + let listSchema = "" + let filterSchema = [ + `input ${convertWordIntoSingular(table.name)}_filter_input {`, + ] + let updateSchema = "" + let insertSchema = "" + let orderSchema = "" + + const tableInfo = await getMysqlTableInfo(table.name, sequelize) let fields: ModelAttributes, any> = {} tableInfo.columns.forEach((column) => { if (column.columnName === "id") return + + if (column.isEnum) { + wertikApp.store.graphql.typeDefs = wertikApp.store.graphql.typeDefs.concat( + generateEnumTypeForGraphql(column) + ) + } + + updateSchema = getUpdateSchema(table, tableInfo) + insertSchema = getInsertSchema(table, tableInfo) + orderSchema = getOrderSchema(table, tableInfo) + + graphqlSchema.push(`${column.columnName}: ${column.graphqlType}`) + + let filter_input = + column.databaseType.toLowerCase() === "enum" + ? `${column.columnName}: ${column.graphqlType}` + : `${ + column.columnName + }: ${column.graphqlType.toLowerCase()}_filter_input` + + filterSchema.push(filter_input) + fields[column.columnName] = { type: column.databaseType, allowNull: column.isNull, @@ -57,6 +95,8 @@ export const withMysqlDatabase = function (obj: WithMysqlDatabaseProps) { values: column.isEnum ? column.enumValues : null, } }) + graphqlSchema.push("}") + filterSchema.push("}") const tableInstance = sequelize.define( table.name, @@ -69,10 +109,27 @@ export const withMysqlDatabase = function (obj: WithMysqlDatabaseProps) { ...databaseDefaultOptions.sql.defaultTableOptions, } ) + + + wertikApp.models[table.name] = tableInstance + const schemaInformation = { + moduleName: table.name, + tableInstance: tableInstance, + schema: graphqlSchema.join(`\n`), + inputSchema: { + insert: insertSchema || "", + update: updateSchema || "", + list: listSchema, + filters: filterSchema.join("\n"), + order_schema: orderSchema || "", + }, + } + + generateGenerateGraphQLCrud(table, schemaInformation) + models.push(tableInstance) - }); + }) - wLogWithSuccess( `[Wertik-Mysql-Database]`, `Successfully connected to database ${obj.name}` diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 46fe7c3e..9be0e5d1 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -4,6 +4,7 @@ import omit from "lodash.omit" import { defaultApolloGraphqlOptions } from "../utils/defaultOptions" import { ApolloServer } from "apollo-server-express" import graphqlDepthLimit from "graphql-depth-limit" +import prettier from "prettier" import { WithApolloGraphqlProps, @@ -15,22 +16,21 @@ export const withApolloGraphql = (props?: WithApolloGraphqlProps) => { return ({ wertikApp, expressApp, - store, configuration, }: GraphqlInitializeProps) => { const depthLimit = get(props, "validation.depthLimit", 7) props = props ? props : {} - store.graphql.typeDefs = store.graphql.typeDefs.concat( + wertikApp.store.graphql.typeDefs = wertikApp.store.graphql.typeDefs.concat( get(configuration, "graphql.typeDefs", "") ) - store.graphql.resolvers.Query = { - ...store.graphql.resolvers.Query, + wertikApp.store.graphql.resolvers.Query = { + ...wertikApp.store.graphql.resolvers.Query, ...get(configuration, "graphql.resolvers.Query", {}), } - store.graphql.resolvers.Mutation = { - ...store.graphql.resolvers.Mutation, + wertikApp.store.graphql.resolvers.Mutation = { + ...wertikApp.store.graphql.resolvers.Mutation, ...get(configuration, "graphql.resolvers.Mutation", {}), } @@ -39,13 +39,19 @@ export const withApolloGraphql = (props?: WithApolloGraphqlProps) => { if (props && props.storeTypeDefFilePath) { if (fs.existsSync(props.storeTypeDefFilePath)) fs.unlinkSync(props.storeTypeDefFilePath) - fs.writeFileSync(props.storeTypeDefFilePath, store.graphql.typeDefs) + + const formattedTypeDefs = prettier.format(wertikApp.store.graphql.typeDefs, { + filepath: props.storeTypeDefFilePath, + semi: false, + parser: "graphql", + }) + fs.writeFileSync(props.storeTypeDefFilePath, formattedTypeDefs) } const GraphqlApolloServer = new ApolloServer({ - typeDefs: store.graphql.typeDefs, + typeDefs: wertikApp.store.graphql.typeDefs, resolvers: { - ...store.graphql.resolvers, + ...wertikApp.store.graphql.resolvers, }, ...defaultApolloGraphqlOptions, ...omit(options, ["context"]), diff --git a/src/index.ts b/src/index.ts index 528dd028..e81e20b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import get from "lodash.get" import express from "express" -import store, { wertikApp } from "./store" -import { applyRelationshipsFromStoreToDatabase } from "./database/database" +import { wertikApp } from "./store" +import { applyRelationshipsFromStoreToDatabase } from "./database/helpers" import { emailSender } from "./mailer/index" import http from "http" import { WertikConfiguration, WertikApp } from "./types" @@ -9,7 +9,7 @@ import { initializeBullBoard } from "./queue/index" import { wLogWithInfo, wLogWithSuccess } from "./utils/log" import { validateModules } from "./modules/modules" -export * from "./database/database" +export * from "./database/mysql/mysql" export * from "./modules/modules" export * from "./graphql" export * from "./mailer" @@ -21,7 +21,6 @@ export * from "./queue" export * from "./redis" export * from "./logger" export * from "./database/mysql/mysql" -export * from "./database/database" const Wertik: (configuration?: WertikConfiguration) => Promise = ( configuration: WertikConfiguration @@ -99,7 +98,6 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( wertikApp.modules[moduleName] = await configuration.modules[ moduleName ]({ - store: store, configuration: configuration, app: wertikApp, }) @@ -133,7 +131,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( wertikApp.logger = configuration.logger } - applyRelationshipsFromStoreToDatabase(store, wertikApp) + applyRelationshipsFromStoreToDatabase(wertikApp) expressApp.get("/w/info", function (req, res) { res.json({ @@ -147,14 +145,9 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( configuration, }) - if (wertikApp?.models) { - store.database.models = wertikApp.models - } - if (configuration.graphql) { wertikApp.graphql = configuration.graphql({ wertikApp: wertikApp, - store: store, configuration: configuration, expressApp: expressApp, }) diff --git a/src/modules/modules.ts b/src/modules/modules.ts index ad8f5f72..258a536e 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -39,7 +39,7 @@ export const withModule = (moduleProps: WithModuleProps) => { store.modules.push(moduleProps) let currentModuleRelationships = [] let tableInstance: ModelStatic> - let graphqlSchema = [`type ${moduleProps.name}Module {`] + let graphqlSchema = [`type ${moduleProps.name} {`] let listSchema = "" let filterSchema = [ `input ${convertWordIntoSingular( @@ -287,7 +287,7 @@ export const withModule = (moduleProps: WithModuleProps) => { } if (useDatabase) { - generateGenerateGraphQLCrud(moduleProps, schemaInformation, store) + generateGenerateGraphQLCrud(moduleProps, schemaInformation) app.models[moduleProps.name] = tableInstance } diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 62de1f59..603c7eab 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -1,9 +1,9 @@ import get from "lodash.get" import { WithModuleProps } from "../types/modules" -import { TableInfo } from "../types/database" +import { SqlTable, TableInfo } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" import crud from "../crud" -import store from "../store" +import { wertikApp } from "../store" import pluralize from "pluralize" import snackCase from "lodash.snakecase" import isPlainObject from "lodash.isplainobject" @@ -65,12 +65,12 @@ export const getGraphQLTypeNameFromSqlType = ( } export const getUpdateSchema = ( - module: WithModuleProps, + table: SqlTable, tableInfo: TableInfo ) => { - const optionsUpdateSchema = get(module, "graphql.updateSchema", "") + const optionsUpdateSchema = get(table, "graphql.updateSchema", "") if (optionsUpdateSchema) return optionsUpdateSchema - let updateSchema = [`input update${module.name}Input {`] + let updateSchema = [`input update_${table.name}_input {`] tableInfo.columns.forEach((column) => { if (column.columnName !== "id" && !column.isDateColumn) { updateSchema.push( @@ -84,11 +84,11 @@ export const getUpdateSchema = ( } export const getInsertSchema = ( - module: WithModuleProps, + table: SqlTable, tableInfo: TableInfo ) => { - const optionsInsertSchema = get(module, "graphql.createSchema", "") - const rowsFieldName = convertWordIntoPlural(module.name) + const optionsInsertSchema = get(table, "graphql.createSchema", "") + const rowsFieldName = convertWordIntoPlural(table.name) if (optionsInsertSchema) return optionsInsertSchema let insertSchema = [`input insert_${rowsFieldName}_input {`] tableInfo.columns.forEach((column) => { @@ -103,12 +103,12 @@ export const getInsertSchema = ( return insertSchema.join("\n") } -export const getOrderSchema = (module: WithModuleProps, tableInfo) => { +export const getOrderSchema = (table: SqlTable, tableInfo) => { let orderSchema = [ - `input ${convertWordIntoSingular(module.name)}_order_input {`, + `input ${convertWordIntoSingular(table.name)}_order_input {`, ] - let relationships = store.database.relationships.filter( - (c) => c.currentModule === module.name + let relationships = wertikApp.store.database.relationships.filter( + (c) => c.currentModule === table.name ) tableInfo.columns.forEach((column) => { orderSchema.push(`${column.columnName}: order_by`) @@ -127,13 +127,12 @@ export const generateEnumTypeForGraphql = (column: TableInfo["columns"][0]) => { export const generateGenerateGraphQLCrud = ( props, - schemaInformation, - store + schemaInformation ) => { - const { graphql } = crud(props, schemaInformation, store) + const { graphql } = crud(props, schemaInformation, wertikApp.store) const resolvers = graphql.generateCrudResolvers() - store.graphql.typeDefs = store.graphql.typeDefs.concat( + wertikApp.store.graphql.typeDefs = wertikApp.store.graphql.typeDefs.concat( `\n ${schemaInformation.schema} \n ${schemaInformation.inputSchema.filters} \n ${schemaInformation.inputSchema.insert} @@ -142,20 +141,20 @@ export const generateGenerateGraphQLCrud = ( ` ) - store.graphql.typeDefs = store.graphql.typeDefs.concat( + wertikApp.store.graphql.typeDefs = wertikApp.store.graphql.typeDefs.concat( `\n ${graphql.generateQueriesCrudSchema()}` ) - store.graphql.typeDefs = store.graphql.typeDefs.concat( + wertikApp.store.graphql.typeDefs = wertikApp.store.graphql.typeDefs.concat( `\n ${graphql.generateMutationsCrudSchema()}` ) - store.graphql.resolvers.Query = { - ...store.graphql.resolvers.Query, + wertikApp.store.graphql.resolvers.Query = { + ...wertikApp.store.graphql.resolvers.Query, ...resolvers.Query, } - store.graphql.resolvers.Mutation = { - ...store.graphql.resolvers.Mutation, + wertikApp.store.graphql.resolvers.Mutation = { + ...wertikApp.store.graphql.resolvers.Mutation, ...resolvers.Mutation, } } @@ -169,7 +168,7 @@ export const getRelationalFieldsRequestedInQuery = ( ) => { const fields = Object.keys(requestedFields) // Filter all relationships for provided modules, based on fields provided filter out those relationships. - const relationalFields = store.database.relationships + const relationalFields = wertikApp.store.database.relationships .filter((c) => c.currentModule === module.name) .filter((relationship) => fields.includes(relationship.graphqlKey)) return relationalFields @@ -177,8 +176,8 @@ export const getRelationalFieldsRequestedInQuery = ( export const generateRequestedFieldsFromGraphqlInfo = (info) => { const keys = [ - ...store.database.relationships.map((c) => c.graphqlKey), - ...store.graphql.graphqlKeys, + ...wertikApp.store.database.relationships.map((c) => c.graphqlKey), + ...wertikApp.store.graphql.graphqlKeys, "__typename", "__arguments", ] diff --git a/src/store.ts b/src/store.ts index 6907a660..c9a43bf7 100644 --- a/src/store.ts +++ b/src/store.ts @@ -36,65 +36,39 @@ export const wertikApp: WertikApp = { }, redis: {}, logger: null, -} - -const store: { - graphql: { - graphqlKeys: string[] - typeDefs: string - resolvers: { - Query: { - [key: string]: Function - } - Mutation: { - [key: string]: Function - } - [key: string]: { - [key: string]: Function | string | number | boolean | object | any - } - } - } - database: { - relationships: StoreDatabaseRelationship[] - models: { - [key: string]: any - } - } - modules: WithModuleProps[] -} = { - graphql: { - graphqlKeys: [], - typeDefs: ` - ${generalSchema} - type Response { - message: String - version: String - } - type Query { - version: String - } - type Mutation { - version: String - } - schema { - query: Query - mutation: Mutation - } - `, - resolvers: { - Query: { - version: () => require("../../package.json").version, - }, - Mutation: { - version: () => require("../../package.json").version, + store: { + graphql: { + graphqlKeys: [], + typeDefs: ` + ${generalSchema} + type Response { + message: String + version: String + } + type Query { + version: String + } + type Mutation { + version: String + } + schema { + query: Query + mutation: Mutation + } + `, + resolvers: { + Query: { + version: () => require("../../package.json").version, + }, + Mutation: { + version: () => require("../../package.json").version, + }, }, }, - }, - database: { - relationships: [], - models: {}, - }, - modules: [], + database: { + relationships: [], + models: {}, + }, + modules: [], + } } - -export default store diff --git a/src/types/index.ts b/src/types/index.ts index 9e08fd53..f04c2b76 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -63,6 +63,7 @@ export interface WertikConfiguration { [key: string]: () => Promise<{ credentials: WithMysqlDatabaseProps instance: Sequelize, + models: WertikModule["tableInstance"][] }> } /** @@ -71,7 +72,6 @@ export interface WertikConfiguration { */ modules?: { [key: string]: (options: { - store: Store configuration: WertikConfiguration app: WertikApp }) => Promise @@ -133,7 +133,6 @@ export interface WertikConfiguration { * Graphql */ graphql?: (options: { - store: Store configuration: WertikConfiguration wertikApp: WertikApp expressApp: any @@ -189,6 +188,30 @@ export interface WertikApp { modules: { [key: string]: WertikModule } + store: { + graphql: { + graphqlKeys: string[] + typeDefs: string + resolvers: { + Query: { + [key: string]: Function + } + Mutation: { + [key: string]: Function + } + [key: string]: { + [key: string]: Function | string | number | boolean | object | any + } + } + } + database: { + relationships: any[] + models: { + [key: string]: any + } + } + modules: WithModuleProps[] + } database: { [key: string]: { credentials: { @@ -199,6 +222,7 @@ export interface WertikApp { host: string } instance: Sequelize, + models: WertikModule["tableInstance"][], } } models: { From f6fe2e59990b0dd54508f6f641d71abb8b4bea95 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sun, 14 Apr 2024 23:44:23 +0500 Subject: [PATCH 51/52] v3.4 tables in database almost done --- src/crud/index.ts | 6 ++-- src/database/eagerLoadingGraphqlQuery.ts | 32 ++++++++++---------- src/database/helpers.ts | 37 +++++++++++++++++++++--- src/database/mysql/mysql.ts | 2 +- src/devServer.ts | 11 ++++++- src/index.ts | 1 + src/modules/modulesHelpers.ts | 9 +++--- src/types/database.ts | 9 ------ src/types/index.ts | 3 +- 9 files changed, 70 insertions(+), 40 deletions(-) diff --git a/src/crud/index.ts b/src/crud/index.ts index 0f95d37d..1a8decca 100644 --- a/src/crud/index.ts +++ b/src/crud/index.ts @@ -203,12 +203,13 @@ export default function (table, schemaInformation, store) { convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }), args, - table + table.name ) const find = await schemaInformation.tableInstance.findOne({ where: omit(where, keys), attributes: generateRequestedFieldsFromGraphqlInfo( + schemaInformation.tableInstance.tableName, graphqlFields(info) ), include: convertFieldsIntoInclude.include, @@ -237,7 +238,7 @@ export default function (table, schemaInformation, store) { convertGraphqlRequestedFieldsIntoInclude( graphqlFields(info, {}, { processArguments: true }), args, - table + table.name ) return await paginate( @@ -246,6 +247,7 @@ export default function (table, schemaInformation, store) { convertFieldsIntoInclude.include, { attributes: generateRequestedFieldsFromGraphqlInfo( + schemaInformation.tableInstance.tableName, graphqlFields(info).rows ), }, diff --git a/src/database/eagerLoadingGraphqlQuery.ts b/src/database/eagerLoadingGraphqlQuery.ts index de180333..f1848e86 100644 --- a/src/database/eagerLoadingGraphqlQuery.ts +++ b/src/database/eagerLoadingGraphqlQuery.ts @@ -4,6 +4,7 @@ import get from "lodash.get" import has from "lodash.has" import convertFiltersIntoSequelizeObject from "../utils/convertFiltersIntoSequelizeObject" import { generateRequestedFieldsFromGraphqlInfo } from "../modules/modulesHelpers" +import { SqlTable } from "src/types/database" const clean = (cleanObject) => { let recursion = (_obj) => { @@ -29,21 +30,21 @@ const clean = (cleanObject) => { export const convertGraphqlRequestedFieldsIntoInclude = ( graphqlFields = {}, args: any = {}, - module: any = {} + tableName: string = "" ) => { let order = [] let depth = [] graphqlFields = clean(graphqlFields) - const currentModuleRelationships = wertikApp.store.database.relationships.filter( - (f) => f.currentModule == module.name - ) - - const currentModuleRelationshipsKeys = currentModuleRelationships.map( - (c) => c.graphqlKey - ) - const allRelationshipKeys = wertikApp.store.database.relationships.map( - (c) => c.graphqlKey - ) + let currentModel = wertikApp.models[tableName]; + let currentModuleRelationshipsKeys =[] + let allRelationshipKeys = [] + + for (const [modelName, model] of Object.entries(wertikApp.models)) { + for (const [key, relationship] of Object.entries(model.associations)) { + allRelationshipKeys.push(key) + currentModuleRelationshipsKeys.push(key) + } + } const requiredFilters = currentModuleRelationshipsKeys.filter((c) => Object.keys(args.where ?? {}).includes(c) @@ -58,17 +59,14 @@ export const convertGraphqlRequestedFieldsIntoInclude = ( Object.keys(_obj).forEach((key) => { if (allRelationshipKeys.includes(key)) { + currentModel = currentModel.associations[key].target; depth.push(key) let _localDepth = [...JSON.parse(JSON.stringify(depth))] - const relationship = wertikApp.store.database.relationships.find( - (c) => c.graphqlKey === key - ) - const sequelizeModel = wertikApp.models[relationship.referencedModule] const includeParams: { [key: string]: any } = { required: false, - model: sequelizeModel, + model: wertikApp.models[currentModel.tableName], as: key, - attributes: generateRequestedFieldsFromGraphqlInfo(_obj[key]), + attributes: generateRequestedFieldsFromGraphqlInfo(currentModel.tableName,_obj[key]), include: Object.keys(_obj[key]).length > 0 ? recursion(_obj[key]) : [], } diff --git a/src/database/helpers.ts b/src/database/helpers.ts index d2f51000..d671c822 100644 --- a/src/database/helpers.ts +++ b/src/database/helpers.ts @@ -1,4 +1,4 @@ -import { MysqlColumnInfoDescribeTable } from "src/types/database" +import { MysqlColumnInfoDescribeTable, SqlTable } from "../types/database" import { capitalizeFirstLetter } from "../utils/capitalizeFirstLetter" import { numberTypes, @@ -7,8 +7,9 @@ import { enumTypes, jsonTypes, } from "./mysql/getTableInfo" -import { WertikApp } from "src/types" +import { WertikApp } from "../types" import get from "lodash.get" +import { wLogWithError, wLogWithInfo } from "../utils/log" export const convertDatabaseTypeIntoGraphqlType = ( columnInfo: MysqlColumnInfoDescribeTable, @@ -82,7 +83,35 @@ export const applyRelationshipsFromStoreToDatabase = async ( app: WertikApp ) => { Object.keys(app.database).forEach(dbName => { - let db = app.database[dbName] - console.log(db.credentials) + let db = app.database[dbName]; + const tables = db?.credentials?.tables || [] + tables.forEach(table => { + const currentModel = app.models[table.name]; + + for (const [relationshipType, relationships] of Object.entries(table.relationships|| {})) { + for (const [relatedTableName, relationshipOptions] of Object.entries(table.relationships[relationshipType] || {})) { + const relatedTable = app.models[relatedTableName]; + if (!relatedTable) { + wLogWithError(`[DB] Related table not found:`, `model '${relatedTableName}' not found for relationship '${relationshipType}' in table '${table.name}'`) + process.exit() + } + wLogWithInfo(`[DB] Applying relationship:`, `${table.name}.${relationshipType}(${relatedTable.tableName},${JSON.stringify(relationshipOptions)})`) + currentModel[relationshipType](relatedTable, relationshipOptions) + const isManyRelationship = ['hasMany','belongsToMany'].includes(relationshipType) + if (isManyRelationship) { + app.store.graphql.typeDefs = app.store.graphql.typeDefs.concat(` + extend type ${table.name} { + ${relationshipOptions.as}(offset: Int, limit: Int, where: ${table.name}_filter_input, order: ${table.name}_order_input): [${relatedTableName}] + } + `) + }else { + app.store.graphql.typeDefs = app.store.graphql.typeDefs.concat(` + extend type ${table.name} { + ${relationshipOptions.as}: ${relatedTableName} + }`) + } + } + } + }); }); } diff --git a/src/database/mysql/mysql.ts b/src/database/mysql/mysql.ts index 47ede29d..af045013 100644 --- a/src/database/mysql/mysql.ts +++ b/src/database/mysql/mysql.ts @@ -64,7 +64,7 @@ export const withMysqlDatabase = function (obj: WithMysqlDatabaseProps) { let fields: ModelAttributes, any> = {} tableInfo.columns.forEach((column) => { - if (column.columnName === "id") return + // if (column.columnName === "id") return if (column.isEnum) { wertikApp.store.graphql.typeDefs = wertikApp.store.graphql.typeDefs.concat( diff --git a/src/devServer.ts b/src/devServer.ts index eb8b1d9d..2d35c464 100644 --- a/src/devServer.ts +++ b/src/devServer.ts @@ -29,7 +29,7 @@ wertik({ name: "user", relationships: { hasMany: { - products: { + product: { as: "products", foreignKey: "user_id", sourceKey: "id", @@ -39,6 +39,15 @@ wertik({ }, { name: "product", + relationships: { + hasOne: { + user: { + as: "user", + foreignKey: "id", + sourceKey: "user_id", + } + } + } }, ], }), diff --git a/src/index.ts b/src/index.ts index e81e20b3..2b1f428c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -84,6 +84,7 @@ const Wertik: (configuration?: WertikConfiguration) => Promise = ( if (configuration.database) { for (const databaseName of Object.keys(configuration.database || {})) { try { + //@ts-ignore wertikApp.database[databaseName] = await configuration.database[ databaseName ]() diff --git a/src/modules/modulesHelpers.ts b/src/modules/modulesHelpers.ts index 603c7eab..44f6ee68 100644 --- a/src/modules/modulesHelpers.ts +++ b/src/modules/modulesHelpers.ts @@ -174,15 +174,14 @@ export const getRelationalFieldsRequestedInQuery = ( return relationalFields } -export const generateRequestedFieldsFromGraphqlInfo = (info) => { - const keys = [ - ...wertikApp.store.database.relationships.map((c) => c.graphqlKey), - ...wertikApp.store.graphql.graphqlKeys, +export const generateRequestedFieldsFromGraphqlInfo = (tableName, info) => { + const keysToIgnore = [ + ...Object.keys(wertikApp.models[tableName].associations), "__typename", "__arguments", ] - return Object.keys(info).filter((c) => !keys.includes(c)) + return Object.keys(info).filter((c) => !keysToIgnore.includes(c)) } export const convertWordIntoSingular = (moduleName) => { diff --git a/src/types/database.ts b/src/types/database.ts index acea9a36..f6874304 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -32,33 +32,24 @@ export interface SqlTable { hasOne?: { [tableName: string]: { as: string, - foreignKey: string, - targetKey: string, [key: string]: any } }, hasMany?: { [tableName: string]: { as: string, - foreignKey: string, - sourceKey: string, [key: string]: any } }, belongsTo?: { [tableName: string]: { as: string, - foreignKey: string, - targetKey: string, [key: string]: any } }, belongsToMany?: { [tableName: string]: { as: string, - through: string, - foreignKey: string, - otherKey: string, [key: string]: any } } diff --git a/src/types/index.ts b/src/types/index.ts index f04c2b76..47298db1 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -219,7 +219,8 @@ export interface WertikApp { name: string password: string username: string - host: string + host: string, + tables: WithMysqlDatabaseProps['tables'] } instance: Sequelize, models: WertikModule["tableInstance"][], From 33b88d9ca68ad3420a1f3cd0e2fdb0de247bcf72 Mon Sep 17 00:00:00 2001 From: Ilyas Karim Date: Sun, 12 May 2024 08:00:58 +0500 Subject: [PATCH 52/52] Update modules.ts --- src/modules/modules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/modules.ts b/src/modules/modules.ts index 258a536e..dcc19c60 100644 --- a/src/modules/modules.ts +++ b/src/modules/modules.ts @@ -24,7 +24,7 @@ import has from "lodash.has" /** * Wertik js module - * @param props see interface UseModuleProps + * @param moduleProps */ export const withModule = (moduleProps: WithModuleProps) => { return async ({