From 1ef0a58ce9957a7934dbfbf60cd6691249bf91fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Mon, 22 Jun 2020 15:32:20 +0200 Subject: [PATCH 01/17] feat: skip computed fields when using the graphql-codegen/typescript-mongodb --- index.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 6249240..44278f0 100755 --- a/index.js +++ b/index.js @@ -12,10 +12,11 @@ program .option('--destDirPath [value]', 'dir you want to store the generated queries') .option('--depthLimit [value]', 'query depth you want to limit(The default is 100)') .option('--typesPath [value]', 'path to your generated typescript file with GraphQL Types') + .option('--withMongo [value]', 'set as true if you are using @graphql-codegen/typescript-mongodb') .parse(process.argv); console.log(); -const { schemaFilePath, destDirPath, typesPath, depthLimit = 100 } = program; +const { schemaFilePath, destDirPath, typesPath, depthLimit = 100, withMongo = false } = program; const pathToDestDir = `${process.cwd()}${destDirPath}`; const pathToTypes = `${process.cwd()}${typesPath}`; @@ -75,12 +76,21 @@ const generateQuery = ( let queryStr = ''; let childQuery = ''; + // const filtered = curType.toConfig().astNode.fields.filter(f => !f.directives.find(d => (d.name.value === "column" || d.name.value === "id" || d.name.value === "embedded" || d.name.value === "link"))) + if (curType.getFields) { const crossReferenceKey = `${curParentName}To${curName}Key`; if (crossReferenceKeyList.indexOf(crossReferenceKey) !== -1 || curDepth > depthLimit) return ''; crossReferenceKeyList.push(crossReferenceKey); const childKeys = Object.keys(curType.getFields()); childQuery = childKeys + .filter(k => { + if (withMongo) { + return curType.getFields()[k].astNode.directives.find(d => (d.name.value === "column" || d.name.value === "id" || d.name.value === "embedded" || d.name.value === "link")); + } else { + return true; + } + }) .map((cur) => generateQuery(cur, curType, curName, argumentList, crossReferenceKeyList, curDepth + 1).queryStr) .filter((cur) => cur) .join('\n'); From 58535bda729a5f7a4104eca1c30025380daa029f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Mon, 22 Jun 2020 17:46:12 +0200 Subject: [PATCH 02/17] working generation for use cases --- generateModule.js | 47 +- package-lock.json | 504 ++-------------------- parseGraphql/parseGraphql.js | 18 +- parseGraphql/parseGraphql.spec.js | 47 +- templates/IModuleNameContext.handlebars | 3 +- templates/getModuleNameContext.handlebars | 1 - templates/mutation.handlebars | 3 +- templates/mutation.spec.handlebars | 4 +- templates/query.handlebars | 3 +- templates/query.spec.handlebars | 7 +- templates/startupConfig.handlebars | 2 +- templates/types.handlebars | 4 +- templates/useCase.handlebars | 12 + templates/useCase.spec.handlebars | 20 + 14 files changed, 158 insertions(+), 517 deletions(-) create mode 100644 templates/useCase.handlebars create mode 100644 templates/useCase.spec.handlebars diff --git a/generateModule.js b/generateModule.js index 9aaa2bb..c3fed46 100755 --- a/generateModule.js +++ b/generateModule.js @@ -32,9 +32,9 @@ modules.forEach((module) => { createModuleResolvers(); - const createQuery = (queryName) => { + const createQuery = (queryName, hasArguments) => { const templateName = './templates/query.handlebars'; - const context = { queryName, moduleName }; + const context = { queryName, moduleName, hasArguments }; const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/queries/`; const fileName = `${queryName}Query.ts`; const keepIfExists = true; @@ -50,17 +50,39 @@ modules.forEach((module) => { saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }; + const createUseCase = (name, hasArguments, variables) => { + const templateName = './templates/useCase.handlebars'; + const context = { name, moduleName, hasArguments, variables }; + const filePath = `${projectMainPath}/src/modules/${moduleName}/useCases/`; + const fileName = `${name}.ts`; + const keepIfExists = true; + saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + }; + + const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1); + + const createUseCaseSpec = (name, hasArguments, variables) => { + const templateName = './templates/useCase.spec.handlebars'; + const context = { name, moduleName, hasArguments, variables, capitalizedName: capitalize(name) }; + const filePath = `${projectMainPath}/src/modules/${moduleName}/useCases/`; + const fileName = `${name}.spec.ts`; + const keepIfExists = true; + saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + }; + if (module.queries && module.queries.length) { shelljs.mkdir('-p', `${projectMainPath}/src/modules/${moduleName}/graphql/queries`); - module.queries.forEach(({ name }) => { - createQuery(name); - createQuerySpec(name); + module.queries.forEach(({ name, hasArguments, variables }) => { + createQuery(name, hasArguments); + createUseCase(`query${name}`, hasArguments, variables); + createUseCaseSpec(`query${name}`, hasArguments, variables); + // createQuerySpec(name); }); } - const createMutation = (mutationName) => { + const createMutation = (mutationName, hasArguments) => { const templateName = './templates/mutation.handlebars'; - const context = { mutationName, moduleName }; + const context = { mutationName, moduleName, hasArguments }; const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/mutations/`; const fileName = `${mutationName}Mutation.ts`; const keepIfExists = true; @@ -78,9 +100,11 @@ modules.forEach((module) => { if (module.mutations && module.mutations.length) { shelljs.mkdir('-p', `${projectMainPath}/src/modules/${moduleName}/graphql/mutations`); - module.mutations.forEach(({ name }) => { - createMutation(name); - createMutationSpec(name); + module.mutations.forEach(({ name, hasArguments, variables }) => { + createMutation(name, hasArguments); + createUseCase(`mutation${name}`, hasArguments, variables, ); + createUseCaseSpec(`mutation${name}`, hasArguments, variables,); + // createMutationSpec(name); }); } }); @@ -144,7 +168,6 @@ const createGetModuleNameContexts = () => { createGetModuleNameContexts(); // typeResolvers.handlebars - const createTypeResolvers = () => { modules.forEach(({ name, typeDefinitions, types }) => { if (types) { @@ -166,9 +189,7 @@ const createTypeResolvers = () => { saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }); } - }); }; createTypeResolvers(); - diff --git a/package-lock.json b/package-lock.json index c31a8e3..5e6cb86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,9 @@ { "name": "gql-typescript-generator", - "version": "6.0.0", + "version": "7.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { - "@apollo/federation": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@apollo/federation/-/federation-0.16.2.tgz", - "integrity": "sha512-pjTkcl1KGxLZOPpVyTygZNuLxZJCCMvGVonPJMoFzQYt63/o0DwpwbcNlbvpdryWjjFgvi5diqJxRFGuxndEPA==", - "requires": { - "apollo-graphql": "^0.4.0", - "apollo-server-env": "^2.4.4", - "core-js": "^3.4.0", - "lodash.xorby": "^4.7.0" - } - }, "@babel/code-frame": { "version": "7.0.0", "resolved": "http://registry.npm.taobao.org/@babel/code-frame/download/@babel/code-frame-7.0.0.tgz", @@ -1217,28 +1206,8 @@ "@types/node": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.1.tgz", - "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==" - }, - "@types/node-fetch": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", - "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", - "requires": { - "@types/node": "*", - "form-data": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } + "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==", + "dev": true }, "@types/normalize-package-data": { "version": "2.4.0", @@ -1366,35 +1335,6 @@ "picomatch": "^2.0.4" } }, - "apollo-env": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.6.5.tgz", - "integrity": "sha512-jeBUVsGymeTHYWp3me0R2CZRZrFeuSZeICZHCeRflHTfnQtlmbSXdy5E0pOyRM9CU4JfQkKDC98S1YglQj7Bzg==", - "requires": { - "@types/node-fetch": "2.5.7", - "core-js": "^3.0.1", - "node-fetch": "^2.2.0", - "sha.js": "^2.4.11" - } - }, - "apollo-graphql": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.4.4.tgz", - "integrity": "sha512-i012iRKT5nfsOaNMx4MTwHw2jrlyaF1zikpejxsGHsKIf3OngGvGh3pyw20bEmwj413OrNQpRxvvIz5A7W/8xw==", - "requires": { - "apollo-env": "^0.6.5", - "lodash.sortby": "^4.7.0" - } - }, - "apollo-server-env": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.4.4.tgz", - "integrity": "sha512-c2oddDS3lwAl6QNCIKCLEzt/dF9M3/tjjYRVdxOVN20TidybI7rAbnT4QOzf4tORnGXtiznEAvr/Kc9ahhKADg==", - "requires": { - "node-fetch": "^2.1.2", - "util.promisify": "^1.0.0" - } - }, "argparse": { "version": "1.0.10", "resolved": "http://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz", @@ -1479,7 +1419,8 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "atob": { "version": "2.1.2", @@ -1974,6 +1915,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -2015,11 +1957,6 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -2125,6 +2062,7 @@ "version": "1.1.3", "resolved": "http://registry.npm.taobao.org/define-properties/download/define-properties-1.1.3.tgz", "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", + "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -2186,7 +2124,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "detect-newline": { "version": "3.1.0", @@ -2925,7 +2864,8 @@ "function-bind": { "version": "1.1.1", "resolved": "http://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -3098,6 +3038,7 @@ "version": "1.0.3", "resolved": "http://registry.npm.taobao.org/has/download/has-1.0.3.tgz", "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -3111,7 +3052,8 @@ "has-symbols": { "version": "1.0.0", "resolved": "http://registry.npm.taobao.org/has-symbols/download/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true }, "has-value": { "version": "1.0.0", @@ -3425,7 +3367,8 @@ "is-date-object": { "version": "1.0.1", "resolved": "http://registry.npm.taobao.org/is-date-object/download/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true }, "is-descriptor": { "version": "0.1.6", @@ -3556,6 +3499,7 @@ "version": "1.0.2", "resolved": "http://registry.npm.taobao.org/is-symbol/download/is-symbol-1.0.2.tgz", "integrity": "sha1-oFX2rlcZLK7jKeeoYBGLSXqVDzg=", + "dev": true, "requires": { "has-symbols": "^1.0.0" } @@ -5401,12 +5345,8 @@ "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" - }, - "lodash.xorby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.xorby/-/lodash.xorby-4.7.0.tgz", - "integrity": "sha1-nBmm+fBjputT3QPBtocXmYAUY9c=" + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true }, "make-dir": { "version": "3.1.0", @@ -5468,12 +5408,14 @@ "mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true }, "mime-types": { "version": "2.1.27", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, "requires": { "mime-db": "1.44.0" } @@ -5596,11 +5538,6 @@ "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", "dev": true }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5722,15 +5659,11 @@ } } }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, "object-keys": { "version": "1.0.12", "resolved": "http://registry.npm.taobao.org/object-keys/download/object-keys-1.0.12.tgz", - "integrity": "sha1-CcU4VTd1dTEMymL1W7M0q/97PtI=" + "integrity": "sha1-CcU4VTd1dTEMymL1W7M0q/97PtI=", + "dev": true }, "object-visit": { "version": "1.0.1", @@ -5745,6 +5678,7 @@ "version": "4.1.0", "resolved": "http://registry.npm.taobao.org/object.assign/download/object.assign-4.1.0.tgz", "integrity": "sha1-lovxEA15Vrs8oIbwBvhGs7xACNo=", + "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -5764,68 +5698,6 @@ "has": "^1.0.1" } }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -6461,7 +6333,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -6669,15 +6542,6 @@ } } }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "shebang-command": { "version": "1.2.0", "resolved": "http://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz", @@ -7092,256 +6956,6 @@ "strip-ansi": "^4.0.0" } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, "stringify-object-es5": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/stringify-object-es5/-/stringify-object-es5-2.5.0.tgz", @@ -7767,70 +7381,6 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, "uuid": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", diff --git a/parseGraphql/parseGraphql.js b/parseGraphql/parseGraphql.js index b3bc0e6..e6f6eaa 100644 --- a/parseGraphql/parseGraphql.js +++ b/parseGraphql/parseGraphql.js @@ -5,9 +5,23 @@ module.exports = (graphqlString) => { `; let foundQueries = graphqlAST.definitions.find((d) => d.name.value === 'Query'); let foundMutations = graphqlAST.definitions.find((d) => d.name.value === 'Mutation'); + console.dir(foundQueries, { depth: null }); + console.dir(foundMutations, { depth: null }); return { - queries: foundQueries && foundQueries.fields.map((f) => ({ name: f.name.value })), - mutations: foundMutations && foundMutations.fields.map((f) => ({ name: f.name.value })), + queries: + foundQueries && + foundQueries.fields.map((f) => ({ + name: f.name.value, + hasArguments: !!f.arguments.length, + variables: f.arguments.map((a) => a.name.value), + })), + mutations: + foundMutations && + foundMutations.fields.map((f) => ({ + name: f.name.value, + hasArguments: !!f.arguments.length, + variables: f.arguments.map((a) => a.name.value), + })), typeDefinitions: graphqlAST.definitions .filter((d) => ['Mutation', 'Query', 'Subscription'].indexOf(d.name.value) === -1) .filter((d) => ['ObjectTypeDefinition', 'ObjectTypeExtension'].indexOf(d.kind) > -1) diff --git a/parseGraphql/parseGraphql.spec.js b/parseGraphql/parseGraphql.spec.js index 2993800..bedbe1a 100644 --- a/parseGraphql/parseGraphql.spec.js +++ b/parseGraphql/parseGraphql.spec.js @@ -32,7 +32,7 @@ const queryString = gql` test('should return names of the queries', () => { const res = parseGraphql(queryString); - expect(res.queries).toEqual([{ name: 'me' }, { name: 'notMe' }]); + expect(res.queries).toMatchObject([{ name: 'me' }, { name: 'notMe' }]); }); test('should return names of types', () => { @@ -42,13 +42,11 @@ test('should return names of types', () => { test('should return names of the Mutations', () => { const res = parseGraphql(queryString); - expect(res.mutations).toEqual([{ name: 'UserChangeName' }]); + expect(res.mutations).toMatchObject([{ name: 'UserChangeName' }]); }); - test('do not throw if queries not found', () => { const queryString = gql` - extend type Mutation { UserChangeName(name: String!): User! } @@ -73,8 +71,7 @@ test('do not throw if queries not found', () => { } `; parseGraphql(queryString); - -}) +}); test('do not throw if mutations not found', () => { const queryString = gql` @@ -82,7 +79,7 @@ test('do not throw if mutations not found', () => { me: User notMe: User } - + type User { id: ID! name: String @@ -104,8 +101,7 @@ test('do not throw if mutations not found', () => { `; parseGraphql(queryString); - -}) +}); test('do not throw if types not found', () => { const queryString = gql` @@ -113,12 +109,41 @@ test('do not throw if types not found', () => { me: User notMe: User } - + extend type Mutation { UserChangeName(name: String!): User! } + `; + + const res = parseGraphql(queryString); +}); +test('should return arguments list for query', () => { + const queryString = gql` + extend type Query { + UserChangeName(name: String!, age: Int): String! + NoArguments: String! + } `; + const res = parseGraphql(queryString); + expect(res.queries).toMatchObject([ + { name: 'UserChangeName', variables: ['name', 'age'] }, + { name: 'NoArguments', variables: [] }, + ]); +}); + +test('should return arguments list for mutation', () => { + const queryString = gql` + extend type Mutation { + UserChangeName(name: String!, age: Int): String! + NoArguments: String! + } + `; const res = parseGraphql(queryString); -}) + + expect(res.mutations).toMatchObject([ + { name: 'UserChangeName', variables: ['name', 'age'] }, + { name: 'NoArguments', variables: [] }, + ]); +}); diff --git a/templates/IModuleNameContext.handlebars b/templates/IModuleNameContext.handlebars index 379cd78..4698b71 100644 --- a/templates/IModuleNameContext.handlebars +++ b/templates/IModuleNameContext.handlebars @@ -1,3 +1,2 @@ -export interface I{{moduleName}}Context { -{{moduleName}}: {}; +export type I{{moduleName}}Context = { } diff --git a/templates/getModuleNameContext.handlebars b/templates/getModuleNameContext.handlebars index 5dca8f1..a721895 100644 --- a/templates/getModuleNameContext.handlebars +++ b/templates/getModuleNameContext.handlebars @@ -4,5 +4,4 @@ import { I{{moduleName}}Context } from "./I{{moduleName}}Context"; export const get{{moduleName}}Context = (): I{{moduleName}}Context => ({ -{{moduleName}}: {} }); diff --git a/templates/mutation.handlebars b/templates/mutation.handlebars index 3a6a4bc..8e5c65e 100644 --- a/templates/mutation.handlebars +++ b/templates/mutation.handlebars @@ -3,6 +3,5 @@ import { MutationResolvers } from "../../../../graphql/generated"; export const {{mutationName}}Mutation: MutationResolvers = { {{mutationName}}: (parent, args, context) => { -throw new Error("{{mutationName}} resolver not implemented yet"); -}, +return context.{{moduleName}}.mutate{{mutationName}}({{#if hasArguments}}args{{/if}})}, }; diff --git a/templates/mutation.spec.handlebars b/templates/mutation.spec.handlebars index 9eb3865..868844a 100644 --- a/templates/mutation.spec.handlebars +++ b/templates/mutation.spec.handlebars @@ -2,9 +2,9 @@ import { getGqlClientWithMockedContext } from "../../../../graphql/testHelpers/getGqlClientWithMockedContext"; -test("{{mutationName}} Mutation passes and gets the data from the module usecase", async () => { +test.skip("{{mutationName}} Mutation passes and gets the data from the module usecase", async () => { const { mutations, context } = getGqlClientWithMockedContext(); -// td.when(context.{{moduleName}}.contextAction()).thenResolve(); +// td.when(context.{{moduleName}}.mutate{{mutationName}}()).thenResolve(); const result = await mutations.{{mutationName}}(); }); diff --git a/templates/query.handlebars b/templates/query.handlebars index b635971..3a9b216 100644 --- a/templates/query.handlebars +++ b/templates/query.handlebars @@ -2,7 +2,6 @@ import { QueryResolvers } from "../../../../graphql/generated"; export const {{queryName}}Query: QueryResolvers = { {{queryName}}: (parent, args, context) => { - - throw new Error("{{queryName}} resolver not implemented yet"); + return context.{{moduleName}}.query{{queryName}}({{#if hasArguments}}args{{/if}}) }, }; diff --git a/templates/query.spec.handlebars b/templates/query.spec.handlebars index 1590416..f03b72d 100644 --- a/templates/query.spec.handlebars +++ b/templates/query.spec.handlebars @@ -1,10 +1,11 @@ -// import td from "testdouble"; +import td from "testdouble"; import { getGqlClientWithMockedContext } from "../../../../graphql/testHelpers/getGqlClientWithMockedContext"; -test("{{queryName}} Query passes and gets the data from the module usecase", async () => { +test.skip("{{queryName}} Query passes and gets the data from the module usecase", async () => { const { queries, context } = getGqlClientWithMockedContext(); -// td.when(context.{{moduleName}}.contextAction()).thenResolve(); +td.when(context.{{moduleName}}.query{{queryName}}()).thenResolve(); const result = await queries.{{queryName}}(); + }); diff --git a/templates/startupConfig.handlebars b/templates/startupConfig.handlebars index a45b2dc..6549ce0 100644 --- a/templates/startupConfig.handlebars +++ b/templates/startupConfig.handlebars @@ -7,6 +7,6 @@ import { schema } from "./graphql/schema"; {{#each modules}}import { get{{name}}Context } from "./modules/{{name}}/get{{name}}Context";{{/each}} const context = (): MyContext => ({ -{{#each modules}}...get{{name}}Context(),{{/each}} +{{#each modules}}{{name}}: {...get{{name}}Context()},{{/each}} }); export const startupConfig = { schema, context }; diff --git a/templates/types.handlebars b/templates/types.handlebars index 2ffdbdc..62e28b3 100644 --- a/templates/types.handlebars +++ b/templates/types.handlebars @@ -3,4 +3,6 @@ {{#each modules}}import { I{{name}}Context } from "./modules/{{name}}/I{{name}}Context"; {{/each}} -export interface MyContext extends {{#each modules}}I{{name}}Context,{{/each}} {} +export interface MyContext { +{{#each modules}}{{name}}: I{{name}}Context;{{/each}} +} diff --git a/templates/useCase.handlebars b/templates/useCase.handlebars new file mode 100644 index 0000000..7ea875a --- /dev/null +++ b/templates/useCase.handlebars @@ -0,0 +1,12 @@ +import { {{moduleName}}Repository} from "../repository/{{moduleName}}Repository"; +import {I{{moduleName}}Context} from "../I{{moduleName}}Context"; + +export const {{name}} = (repository: {{moduleName}}Repository) => { + +const internal: IListsContext["{{name}}"] = async ({{#if hasArguments}}{ {{#each variables}} {{this}}, {{/each}} }{{/if}}) => { +// Add your business logic here, everything else you can safely ignore, it's wired up for you! +return repository.doSomething(); +}; + +return internal; +} diff --git a/templates/useCase.spec.handlebars b/templates/useCase.spec.handlebars new file mode 100644 index 0000000..55839ac --- /dev/null +++ b/templates/useCase.spec.handlebars @@ -0,0 +1,20 @@ +import td from "testdouble"; +{{#if hasArguments}}import { {{capitalizedName}}Args } from "../../../graphql/generated";{{/if}} +import { {{moduleName}}Repository} from "../repository/{{moduleName}}Repository"; +import { {{name}} } from "./{{name}}"; + +test("{{name}}", async () => { + const repository = td.object<{{moduleName}}Repository>(); + + const resolved: _REPLACE_ME_DbObject[] = []; + td.when(repository.findAll()).thenResolve(resolved); + + {{#if hasArguments}} + const args: {{capitalizedName}}Args = {} + {{/if}} + + const result = await {{name}}(repository)({{#if hasArguments}}args{{/if}}); + + expect(result).toEqual(resolved); +}); + From 9f436721918288a75cb45620210d42de4933e77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Mon, 22 Jun 2020 19:03:29 +0200 Subject: [PATCH 03/17] generate type fields resolvers --- generateModule.js | 51 +++++++++++++++++++++----- parseGraphql/getModuleInfos.js | 2 +- parseGraphql/parseGraphql.js | 2 - templates/typeTypeResolvers.handlebars | 5 ++- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/generateModule.js b/generateModule.js index c3fed46..f1fca09 100755 --- a/generateModule.js +++ b/generateModule.js @@ -1,6 +1,8 @@ #!/usr/bin/env node const finder = require('find-package-json'); const shelljs = require('shelljs'); +const { Source, buildSchema } = require('graphql'); +const fs = require('fs'); const getModuleInfos = require('./parsegraphql/getModuleInfos'); const getModuleNames = require('./parsegraphql/getModuleNames'); @@ -102,8 +104,8 @@ modules.forEach((module) => { shelljs.mkdir('-p', `${projectMainPath}/src/modules/${moduleName}/graphql/mutations`); module.mutations.forEach(({ name, hasArguments, variables }) => { createMutation(name, hasArguments); - createUseCase(`mutation${name}`, hasArguments, variables, ); - createUseCaseSpec(`mutation${name}`, hasArguments, variables,); + createUseCase(`mutation${name}`, hasArguments, variables); + createUseCaseSpec(`mutation${name}`, hasArguments, variables); // createMutationSpec(name); }); } @@ -169,8 +171,16 @@ createGetModuleNameContexts(); // typeResolvers.handlebars const createTypeResolvers = () => { - modules.forEach(({ name, typeDefinitions, types }) => { + modules.forEach(({ name, typeDefinitions, types, schemaString }) => { if (types) { + // XXX TODO read this from the CLI + const typeDef = fs.readFileSync('./schema.graphql'); + + const source = new Source(typeDef); + let schema = buildSchema(source); + // const schema = importSchema(schemaPath, {}, { out: "GraphQLSchema" }); + const typeMap = schema.getTypeMap(); + shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/`); const templateName = './templates/typeResolvers.handlebars'; const context = { type: typeDefinitions }; @@ -180,13 +190,34 @@ const createTypeResolvers = () => { saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); typeDefinitions.forEach((typeDef) => { - const templateName = './templates/typeTypeResolvers.handlebars'; - const context = { typeName: typeDef.name }; - const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; - const fileName = `${typeDef.name}TypeResolvers.ts`; - const keepIfExists = true; - - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + let filtered = []; + const type = schema.getType(typeDef.name); + if (type.astNode) { + filtered = type.astNode.fields.filter( + (f) => + !f.directives.find( + (d) => + d.name.value === 'column' || + d.name.value === 'id' || + d.name.value === 'embedded' || + d.name.value === 'link' + ) + ); + } + + if (filtered.length) { + shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/${typeDef.name}`); + } + filtered.forEach(({name: {value}}) => { + + const templateName = './templates/typeTypeResolvers.handlebars'; + const context = { typeName: typeDef.name, fieldName: value, moduleName: name }; + const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/${typeDef.name}`; + const fileName = `${value}TypeResolvers.ts`; + const keepIfExists = true; + + saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + }) }); } }); diff --git a/parseGraphql/getModuleInfos.js b/parseGraphql/getModuleInfos.js index 333ced1..d2459f2 100644 --- a/parseGraphql/getModuleInfos.js +++ b/parseGraphql/getModuleInfos.js @@ -4,6 +4,6 @@ module.exports = (namesObject) => { return namesObject.map((o) => { const schemaString = fs.readFileSync(o.graphqlFilePath, 'utf8'); const parsedGraphql = parseGraphql(schemaString); - return { name: o.name, ...parsedGraphql, types: parsedGraphql.typeDefinitions.length > 0 }; + return { name: o.name, ...parsedGraphql, types: parsedGraphql.typeDefinitions.length > 0, schemaString}; }); }; diff --git a/parseGraphql/parseGraphql.js b/parseGraphql/parseGraphql.js index e6f6eaa..a124e7c 100644 --- a/parseGraphql/parseGraphql.js +++ b/parseGraphql/parseGraphql.js @@ -5,8 +5,6 @@ module.exports = (graphqlString) => { `; let foundQueries = graphqlAST.definitions.find((d) => d.name.value === 'Query'); let foundMutations = graphqlAST.definitions.find((d) => d.name.value === 'Mutation'); - console.dir(foundQueries, { depth: null }); - console.dir(foundMutations, { depth: null }); return { queries: foundQueries && diff --git a/templates/typeTypeResolvers.handlebars b/templates/typeTypeResolvers.handlebars index f39ff5a..14477f6 100644 --- a/templates/typeTypeResolvers.handlebars +++ b/templates/typeTypeResolvers.handlebars @@ -1,5 +1,6 @@ -import { {{typeName}}Resolvers } from "../../../../graphql/generated"; +import { {{typeName}}Resolvers } from "../../../../../graphql/generated"; export const {{typeName}}TypeResolvers = { - + {{fieldName}}: (parent, args, context) => +context.{{moduleName}}.{{typeName}}_{{fieldName}}(parent), } as {{typeName}}Resolvers; From e8508f3c286cc2c879b99c4288f59d294ef78ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Wed, 24 Jun 2020 11:50:34 +0200 Subject: [PATCH 04/17] dont generate test queries anymore, reorganization --- generateModule.js | 149 ++++++-------- index.js | 208 -------------------- templates/IModuleNameContext.handlebars | 2 - templates/getModuleNameContext.handlebars | 7 - templates/module.graphql | 3 - templates/moduleResolvers.handlebars | 8 +- templates/mutation.handlebars | 6 +- templates/mutation.spec.handlebars | 25 ++- templates/query.handlebars | 7 +- templates/query.spec.handlebars | 21 +- templates/startupConfig.handlebars | 7 +- templates/typeResolvers.handlebars | 7 - templates/typeTypeResolvers.handlebars | 7 +- templates/typeTypeResolvers.spec.handlebars | 24 +++ templates/types.handlebars | 6 +- templates/useCase.handlebars | 12 -- templates/useCase.spec.handlebars | 20 -- 17 files changed, 131 insertions(+), 388 deletions(-) delete mode 100644 templates/IModuleNameContext.handlebars delete mode 100644 templates/getModuleNameContext.handlebars delete mode 100644 templates/module.graphql delete mode 100644 templates/typeResolvers.handlebars create mode 100644 templates/typeTypeResolvers.spec.handlebars delete mode 100644 templates/useCase.handlebars delete mode 100644 templates/useCase.spec.handlebars diff --git a/generateModule.js b/generateModule.js index f1fca09..3d1b659 100755 --- a/generateModule.js +++ b/generateModule.js @@ -10,6 +10,8 @@ const checkIfGitStateClean = require('./helpers/checkIfGitStateClean'); const saveRenderedTemplate = require('./helpers/saveRenderedTemplate'); checkIfGitStateClean(); +const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1); + const f = finder(process.cwd()); const projectMainPath = f .next() @@ -24,15 +26,6 @@ const modules = getModuleInfos(moduleNames); modules.forEach((module) => { const moduleName = module.name; - const createModuleResolvers = () => { - const templateName = './templates/moduleResolvers.handlebars'; - const context = { ...module, moduleName: module.name }; - const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/`; - const fileName = `${moduleName}Resolvers.ts`; - saveRenderedTemplate(templateName, context, filePath, fileName); - }; - - createModuleResolvers(); const createQuery = (queryName, hasArguments) => { const templateName = './templates/query.handlebars'; @@ -43,42 +36,20 @@ modules.forEach((module) => { saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }; - const createQuerySpec = (queryName) => { + const createQuerySpec = (queryName, hasArguments) => { const templateName = './templates/query.spec.handlebars'; - const context = { queryName, moduleName }; + const context = { queryName, moduleName, hasArguments }; const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/queries/`; const fileName = `${queryName}Query.spec.ts`; const keepIfExists = true; saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }; - const createUseCase = (name, hasArguments, variables) => { - const templateName = './templates/useCase.handlebars'; - const context = { name, moduleName, hasArguments, variables }; - const filePath = `${projectMainPath}/src/modules/${moduleName}/useCases/`; - const fileName = `${name}.ts`; - const keepIfExists = true; - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); - }; - - const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1); - - const createUseCaseSpec = (name, hasArguments, variables) => { - const templateName = './templates/useCase.spec.handlebars'; - const context = { name, moduleName, hasArguments, variables, capitalizedName: capitalize(name) }; - const filePath = `${projectMainPath}/src/modules/${moduleName}/useCases/`; - const fileName = `${name}.spec.ts`; - const keepIfExists = true; - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); - }; - if (module.queries && module.queries.length) { shelljs.mkdir('-p', `${projectMainPath}/src/modules/${moduleName}/graphql/queries`); module.queries.forEach(({ name, hasArguments, variables }) => { createQuery(name, hasArguments); - createUseCase(`query${name}`, hasArguments, variables); - createUseCaseSpec(`query${name}`, hasArguments, variables); - // createQuerySpec(name); + createQuerySpec(name, hasArguments); }); } @@ -91,9 +62,9 @@ modules.forEach((module) => { saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }; - const createMutationSpec = (mutationName) => { + const createMutationSpec = (mutationName, hasArguments) => { const templateName = './templates/mutation.spec.handlebars'; - const context = { mutationName, moduleName }; + const context = { mutationName, moduleName, hasArguments }; const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/mutations/`; const fileName = `${mutationName}Mutation.spec.ts`; const keepIfExists = true; @@ -104,9 +75,7 @@ modules.forEach((module) => { shelljs.mkdir('-p', `${projectMainPath}/src/modules/${moduleName}/graphql/mutations`); module.mutations.forEach(({ name, hasArguments, variables }) => { createMutation(name, hasArguments); - createUseCase(`mutation${name}`, hasArguments, variables); - createUseCaseSpec(`mutation${name}`, hasArguments, variables); - // createMutationSpec(name); + createMutationSpec(name, hasArguments); }); } }); @@ -126,7 +95,9 @@ const createTypes = () => { const context = { modules }; const filePath = `${projectMainPath}/src/`; const fileName = `types.ts`; - saveRenderedTemplate(templateName, context, filePath, fileName); + const keepIfExists = true; + + saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }; createTypes(); @@ -136,59 +107,24 @@ const createStartupConfig = () => { const context = { modules }; const filePath = `${projectMainPath}/src/`; const fileName = `startupConfig.ts`; - saveRenderedTemplate(templateName, context, filePath, fileName); -}; + const keepIfExists = true; -createStartupConfig(); - -const createIModuleNameContexts = () => { - modules.forEach(({ name }) => { - const templateName = './templates/IModuleNameContext.handlebars'; - const context = { moduleName: name }; - const filePath = `${projectMainPath}/src/modules/${name}/`; - const fileName = `I${name}Context.ts`; - const keepIfExists = true; - - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); - }); -}; - -createIModuleNameContexts(); - -const createGetModuleNameContexts = () => { - modules.forEach(({ name }) => { - const templateName = './templates/getModuleNameContext.handlebars'; - const context = { moduleName: name }; - const filePath = `${projectMainPath}/src/modules/${name}/`; - const fileName = `get${name}Context.ts`; - const keepIfExists = true; - - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); - }); + saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }; -createGetModuleNameContexts(); -// typeResolvers.handlebars +createStartupConfig(); const createTypeResolvers = () => { - modules.forEach(({ name, typeDefinitions, types, schemaString }) => { + modules.forEach(({ name, typeDefinitions, types, schemaString, queries, mutations }) => { + let typeResolvers = []; if (types) { // XXX TODO read this from the CLI const typeDef = fs.readFileSync('./schema.graphql'); const source = new Source(typeDef); let schema = buildSchema(source); - // const schema = importSchema(schemaPath, {}, { out: "GraphQLSchema" }); - const typeMap = schema.getTypeMap(); shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/`); - const templateName = './templates/typeResolvers.handlebars'; - const context = { type: typeDefinitions }; - const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; - const fileName = `typeResolvers.ts`; - const keepIfExists = false; - - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); typeDefinitions.forEach((typeDef) => { let filtered = []; const type = schema.getType(typeDef.name); @@ -205,21 +141,58 @@ const createTypeResolvers = () => { ); } - if (filtered.length) { - shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/${typeDef.name}`); - } - filtered.forEach(({name: {value}}) => { + filtered.forEach(({ name: { value } }) => { const templateName = './templates/typeTypeResolvers.handlebars'; - const context = { typeName: typeDef.name, fieldName: value, moduleName: name }; - const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/${typeDef.name}`; - const fileName = `${value}TypeResolvers.ts`; + let capitalizedFieldName = capitalize(value); + const context = { + typeName: typeDef.name, + fieldName: value, + moduleName: name, + capitalizedFieldName, + }; + const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; + const fileName = `${typeDef.name}${capitalizedFieldName}.ts`; + const keepIfExists = true; + + saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + }); + + filtered.forEach(({ name: { value }, arguments }) => { + const templateName = './templates/typeTypeResolvers.spec.handlebars'; + let capitalizedFieldName = capitalize(value); + const context = { + typeName: typeDef.name, + fieldName: value, + moduleName: name, + hasArguments: arguments && arguments.length, + capitalizedFieldName, + }; + const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; + const fileName = `${typeDef.name}${capitalizedFieldName}.spec.ts`; const keepIfExists = true; saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); - }) + }); + + if (filtered.length) { + typeResolvers.push({ + typeName: typeDef.name, + fieldName: filtered.map(({ name: { value } }) => ({ name: value, capitalizedName: capitalize(value) })), + }); + } }); } + const moduleName = name; + const createModuleResolvers = () => { + const templateName = './templates/moduleResolvers.handlebars'; + const context = { moduleName, queries, mutations, typeResolvers }; + const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/`; + const fileName = `${moduleName}Resolvers.ts`; + saveRenderedTemplate(templateName, context, filePath, fileName); + }; + + createModuleResolvers(); }); }; diff --git a/index.js b/index.js index 44278f0..b083099 100755 --- a/index.js +++ b/index.js @@ -1,210 +1,2 @@ #!/usr/bin/env node -const { Source, buildSchema } = require('graphql'); -const gql = require('graphql-tag'); - -const fs = require('fs'); -const path = require('path'); -const program = require('commander'); -const del = require('del'); - -program - .option('--schemaFilePath [value]', 'path of your graphql schema file') - .option('--destDirPath [value]', 'dir you want to store the generated queries') - .option('--depthLimit [value]', 'query depth you want to limit(The default is 100)') - .option('--typesPath [value]', 'path to your generated typescript file with GraphQL Types') - .option('--withMongo [value]', 'set as true if you are using @graphql-codegen/typescript-mongodb') - .parse(process.argv); - -console.log(); -const { schemaFilePath, destDirPath, typesPath, depthLimit = 100, withMongo = false } = program; - -const pathToDestDir = `${process.cwd()}${destDirPath}`; -const pathToTypes = `${process.cwd()}${typesPath}`; -const typesRelativePathWithExtension = path.relative(pathToDestDir, pathToTypes); -const typesRelativePath = typesRelativePathWithExtension.replace(path.extname(typesRelativePathWithExtension), ''); - -const typeDef = fs.readFileSync(schemaFilePath); - -let gqlSchema -try { - const source = new Source(typeDef); - gqlSchema = buildSchema(source); -} catch(e) { - const { buildFederatedSchema } = require('@apollo/federation'); - - gqlSchema = buildFederatedSchema([ - { - typeDefs: gql` - ${typeDef} - `, - }, - ]); -} - -del.sync(destDirPath); -path - .resolve(destDirPath) - .split(path.sep) - .reduce((before, cur) => { - const pathTmp = path.join(before, cur + path.sep); - if (!fs.existsSync(pathTmp)) { - fs.mkdirSync(pathTmp); - } - return path.join(before, cur + path.sep); - }, ''); - -/** - * Generate the query for the specified field - * @param curName name of the current field - * @param curParentType parent type of the current field - * @param curParentName parent name of the current field - * @param argumentList list of arguments from all fields - * @param crossReferenceKeyList list of the cross reference - * @param curDepth currentl depth of field - */ -const generateQuery = ( - curName, - curParentType, - curParentName, - argumentList = [], - crossReferenceKeyList = [], // [`${curParentName}To${curName}Key`] - curDepth = 1 -) => { - const field = gqlSchema.getType(curParentType).getFields()[curName]; - const curTypeName = field.type.inspect().replace(/[[\]!]/g, ''); - const curType = gqlSchema.getType(curTypeName); - let queryStr = ''; - let childQuery = ''; - - // const filtered = curType.toConfig().astNode.fields.filter(f => !f.directives.find(d => (d.name.value === "column" || d.name.value === "id" || d.name.value === "embedded" || d.name.value === "link"))) - - if (curType.getFields) { - const crossReferenceKey = `${curParentName}To${curName}Key`; - if (crossReferenceKeyList.indexOf(crossReferenceKey) !== -1 || curDepth > depthLimit) return ''; - crossReferenceKeyList.push(crossReferenceKey); - const childKeys = Object.keys(curType.getFields()); - childQuery = childKeys - .filter(k => { - if (withMongo) { - return curType.getFields()[k].astNode.directives.find(d => (d.name.value === "column" || d.name.value === "id" || d.name.value === "embedded" || d.name.value === "link")); - } else { - return true; - } - }) - .map((cur) => generateQuery(cur, curType, curName, argumentList, crossReferenceKeyList, curDepth + 1).queryStr) - .filter((cur) => cur) - .join('\n'); - } - - if (!(curType.getFields && !childQuery)) { - queryStr = `${' '.repeat(curDepth)}${field.name}`; - if (field.args.length > 0) { - argumentList.push(...field.args); - const argsStr = field.args.map((arg) => `${arg.name}: $${arg.name}`).join(', '); - queryStr += `(${argsStr})`; - } - if (childQuery) { - queryStr += `{\n${childQuery}\n${' '.repeat(curDepth)}}`; - } - } - - /* Union types */ - if (curType.astNode && curType.astNode.kind === 'UnionTypeDefinition') { - const types = curType.getTypes(); - if (types && types.length) { - const indent = `${' '.repeat(curDepth)}`; - const fragIndent = `${' '.repeat(curDepth + 1)}`; - queryStr += '{\n'; - - for (let i = 0, len = types.length; i < len; i++) { - const valueTypeName = types[i]; - const valueType = gqlSchema.getType(valueTypeName); - const unionChildQuery = Object.keys(valueType.getFields()) - .map( - (cur) => generateQuery(cur, valueType, curName, argumentList, crossReferenceKeyList, curDepth + 2).queryStr - ) - .filter((cur) => cur) - .join('\n'); - queryStr += `${fragIndent}... on ${valueTypeName} {\n${unionChildQuery}\n${fragIndent}}\n`; - } - queryStr += `${indent}}`; - } - } - return { queryStr, argumentList }; -}; - -const templateContext = { mutations: [], queries: [], subscriptions: [], pathToTypes: typesRelativePath }; - -/** - * Generate the query for the specified field - * @param obj one of the root objects(Query, Mutation, Subscription) - * @param description description of the current object - */ -const generateFile = (obj, description) => { - let indexJs = "import gql from \"graphql-tag\";\nconst fs = require('fs');\nconst path = require('path');\n\n"; - let outputFolderName; - switch (description) { - case 'Mutation': - outputFolderName = 'mutations'; - break; - case 'Query': - outputFolderName = 'queries'; - break; - case 'Subscription': - outputFolderName = 'subscriptions'; - break; - default: - console.log('[gqlg warning]:', 'description is required'); - } - const writeFolder = path.join(destDirPath, `./${outputFolderName}`); - fs.mkdirSync(writeFolder); - Object.keys(obj) - .filter((t) => t !== '_entities' && t !== '_service') - .forEach((type) => { - console.log(type); - const queryResult = generateQuery(type, description); - let query = queryResult.queryStr; - const field = gqlSchema.getType(description).getFields()[type]; - const args = field.args.concat(queryResult.argumentList); - const argStr = args - .filter((item, pos) => args.indexOf(item) === pos) - .map((arg) => `$${arg.name}: ${arg.type}`) - .join(', '); - query = `${description.toLowerCase()} ${type}${argStr ? `(${argStr})` : ''}{\n${query}\n}`; - fs.writeFileSync(path.join(writeFolder, `./${type}.graphql`), query); - indexJs += `export const ${type} = gql\`$\{fs.readFileSync(path.join(__dirname, '${type}.graphql'), 'utf8')}\`;\n`; - templateContext[outputFolderName].push({ name: type, hasVariables: !!argStr }); - }); - fs.writeFileSync(path.join(writeFolder, 'index.ts'), indexJs); -}; - -if (gqlSchema.getMutationType()) { - generateFile(gqlSchema.getMutationType().getFields(), 'Mutation'); -} else { - console.log('[gqlg warning]:', 'No mutation type found in your schema'); -} - -if (gqlSchema.getQueryType()) { - generateFile(gqlSchema.getQueryType().getFields(), 'Query'); -} else { - console.log('[gqlg warning]:', 'No query type found in your schema'); -} - -if (gqlSchema.getSubscriptionType()) { - generateFile(gqlSchema.getSubscriptionType().getFields(), 'Subscription'); -} else { - console.log('[gqlg warning]:', 'No subscription type found in your schema'); -} -const Handlebars = require('handlebars'); - -Handlebars.registerHelper('toUpperCase', function (s) { - if (typeof s !== 'string') return ''; - return s.charAt(0).toUpperCase() + s.slice(1); -}); - -const rootTemplate = fs.readFileSync(path.join(__dirname, './root.handlebars'), 'utf8'); - -const generateIndex = () => Handlebars.compile(rootTemplate)(templateContext); - -fs.writeFileSync(path.join(destDirPath, 'index.ts'), generateIndex()); require('./generateModule'); diff --git a/templates/IModuleNameContext.handlebars b/templates/IModuleNameContext.handlebars deleted file mode 100644 index 4698b71..0000000 --- a/templates/IModuleNameContext.handlebars +++ /dev/null @@ -1,2 +0,0 @@ -export type I{{moduleName}}Context = { -} diff --git a/templates/getModuleNameContext.handlebars b/templates/getModuleNameContext.handlebars deleted file mode 100644 index a721895..0000000 --- a/templates/getModuleNameContext.handlebars +++ /dev/null @@ -1,7 +0,0 @@ -// Warning, this file is autogenerated, please don't change it manually. -// Your changes will get overwritten. - -import { I{{moduleName}}Context } from "./I{{moduleName}}Context"; - -export const get{{moduleName}}Context = (): I{{moduleName}}Context => ({ -}); diff --git a/templates/module.graphql b/templates/module.graphql deleted file mode 100644 index 2b0cbf4..0000000 --- a/templates/module.graphql +++ /dev/null @@ -1,3 +0,0 @@ -# extend type Query {} -# extend type Mutation {} - diff --git a/templates/moduleResolvers.handlebars b/templates/moduleResolvers.handlebars index 96373c6..b73689d 100644 --- a/templates/moduleResolvers.handlebars +++ b/templates/moduleResolvers.handlebars @@ -1,12 +1,14 @@ {{#each queries}}import { {{name}}Query } from "./queries/{{name}}Query";{{/each}} {{#each mutations}}import { {{name}}Mutation } from "./mutations/{{name}}Mutation";{{/each}} +{{#each typeResolvers}}{{#each fieldName}}import { {{../typeName}}{{capitalizedName}} } from "./types/{{../typeName}}{{capitalizedName}}";{{/each}}{{/each}} {{#if types}}import { typeResolvers } from "./types/typeResolvers";{{/if}} // Warning, this file is autogenerated, please don't change it manually. // Your changes will get overwritten. export const {{moduleName}}Resolvers = { -{{#if types}}...typeResolvers,{{/if}} -{{#if queries.length}}Query: { {{#each queries}}...{{name}}Query,{{/each}} },{{/if}} -{{#if mutations.length}}Mutation: { {{#each mutations}}...{{name}}Mutation,{{/each}} },{{/if}} +{{#each typeResolvers}} + {{typeName}}: { {{#each fieldName}}{{name}}: {{../typeName}}{{capitalizedName}},{{/each}} }, {{/each}} +{{#if queries.length}}Query: { {{#each queries}}{{name}}: {{name}}Query,{{/each}} },{{/if}} +{{#if mutations.length}}Mutation: { {{#each mutations}}{{name}}: {{name}}Mutation,{{/each}} },{{/if}} }; diff --git a/templates/mutation.handlebars b/templates/mutation.handlebars index 8e5c65e..7662864 100644 --- a/templates/mutation.handlebars +++ b/templates/mutation.handlebars @@ -1,7 +1,3 @@ import { MutationResolvers } from "../../../../graphql/generated"; -export const {{mutationName}}Mutation: MutationResolvers = { -{{mutationName}}: (parent, args, context) => { - -return context.{{moduleName}}.mutate{{mutationName}}({{#if hasArguments}}args{{/if}})}, -}; +export const {{mutationName}}Mutation: MutationResolvers["{{mutationName}}"] = (parent, args, context) => { throw new Error("not implemented yet"); } diff --git a/templates/mutation.spec.handlebars b/templates/mutation.spec.handlebars index 868844a..a895de8 100644 --- a/templates/mutation.spec.handlebars +++ b/templates/mutation.spec.handlebars @@ -1,10 +1,21 @@ -// import td from "testdouble"; +import td from "testdouble"; +{{#if hasArguments}}import { Mutation{{mutationName}}Args } from "../../../../graphql/generated";{{/if}} +import { {{mutationName}}Mutation } from "./{{mutationName}}Mutation"; +import {MyContext} from "../../../../types"; -import { getGqlClientWithMockedContext } from "../../../../graphql/testHelpers/getGqlClientWithMockedContext"; +const test{{mutationName}} = ({{#if hasArguments}}variables: Mutation{{mutationName}}Args, {{/if}}context: MyContext) => {{mutationName}}Mutation({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) -test.skip("{{mutationName}} Mutation passes and gets the data from the module usecase", async () => { -const { mutations, context } = getGqlClientWithMockedContext(); -// td.when(context.{{moduleName}}.mutate{{mutationName}}()).thenResolve(); -const result = await mutations.{{mutationName}}(); -}); +test("{{mutationName}}", async () => { + +const context = td.object(); + + // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() + + {{#if hasArguments}} + const variables: Mutation{{mutationName}}Args = {} + {{/if}} + + const result = await test{{mutationName}}({{#if hasArguments}}variables,{{/if}} context); + + }); diff --git a/templates/query.handlebars b/templates/query.handlebars index 3a9b216..bc8cc4f 100644 --- a/templates/query.handlebars +++ b/templates/query.handlebars @@ -1,7 +1,4 @@ import { QueryResolvers } from "../../../../graphql/generated"; -export const {{queryName}}Query: QueryResolvers = { - {{queryName}}: (parent, args, context) => { - return context.{{moduleName}}.query{{queryName}}({{#if hasArguments}}args{{/if}}) -}, -}; +export const {{queryName}}Query: QueryResolvers["{{queryName}}"] = (parent, args, context) => { throw new Error("not implemented yet"); } + diff --git a/templates/query.spec.handlebars b/templates/query.spec.handlebars index f03b72d..491bb9f 100644 --- a/templates/query.spec.handlebars +++ b/templates/query.spec.handlebars @@ -1,11 +1,22 @@ import td from "testdouble"; +{{#if hasArguments}}import { Query{{queryName}}Args } from "../../../../graphql/generated";{{/if}} +import { {{queryName}}Query } from "./{{queryName}}Query"; +import {MyContext} from "../../../../types"; -import { getGqlClientWithMockedContext } from "../../../../graphql/testHelpers/getGqlClientWithMockedContext"; -test.skip("{{queryName}} Query passes and gets the data from the module usecase", async () => { -const { queries, context } = getGqlClientWithMockedContext(); +const test{{queryName}} = ({{#if hasArguments}}variables: Query{{queryName}}Args,{{/if}} context: MyContext) => {{queryName}}Query({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) -td.when(context.{{moduleName}}.query{{queryName}}()).thenResolve(); -const result = await queries.{{queryName}}(); + +test("{{queryName}}", async () => { + + const context = td.object(); + + // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() + + {{#if hasArguments}} + const variables: Query{{queryName}}Args = {} + {{/if}} + + const result = await test{{queryName}}({{#if hasArguments}}variables,{{/if}} context); }); diff --git a/templates/startupConfig.handlebars b/templates/startupConfig.handlebars index 6549ce0..586f2e0 100644 --- a/templates/startupConfig.handlebars +++ b/templates/startupConfig.handlebars @@ -1,12 +1,7 @@ -// Warning, this file is autogenerated, please don't change it manually. -// Your changes will get overwritten. - import { MyContext } from "./types"; import { schema } from "./graphql/schema"; -{{#each modules}}import { get{{name}}Context } from "./modules/{{name}}/get{{name}}Context";{{/each}} - const context = (): MyContext => ({ -{{#each modules}}{{name}}: {...get{{name}}Context()},{{/each}} + }); export const startupConfig = { schema, context }; diff --git a/templates/typeResolvers.handlebars b/templates/typeResolvers.handlebars deleted file mode 100644 index 4d0e45b..0000000 --- a/templates/typeResolvers.handlebars +++ /dev/null @@ -1,7 +0,0 @@ -{{#each type}}import { {{name}}TypeResolvers } from "./{{name}}TypeResolvers";{{/each}} - -export const typeResolvers = { -{{#each type}} -{{name}}: {{name}}TypeResolvers, -{{/each}} -}; diff --git a/templates/typeTypeResolvers.handlebars b/templates/typeTypeResolvers.handlebars index 14477f6..f158361 100644 --- a/templates/typeTypeResolvers.handlebars +++ b/templates/typeTypeResolvers.handlebars @@ -1,6 +1,3 @@ -import { {{typeName}}Resolvers } from "../../../../../graphql/generated"; +import { {{typeName}}Resolvers } from "../../../../graphql/generated"; -export const {{typeName}}TypeResolvers = { - {{fieldName}}: (parent, args, context) => -context.{{moduleName}}.{{typeName}}_{{fieldName}}(parent), -} as {{typeName}}Resolvers; +export const {{typeName}}{{capitalizedFieldName}}: {{typeName}}Resolvers["{{fieldName}}"] = (parent, args, context) => { throw new Error("Not implemented yet"); } diff --git a/templates/typeTypeResolvers.spec.handlebars b/templates/typeTypeResolvers.spec.handlebars new file mode 100644 index 0000000..4dcf829 --- /dev/null +++ b/templates/typeTypeResolvers.spec.handlebars @@ -0,0 +1,24 @@ +import td from "testdouble"; + +import { ResolversParentTypes, {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "../../../../graphql/generated"; +import { {{typeName}}{{capitalizedFieldName}} } from "./{{typeName}}{{capitalizedFieldName}}"; +import {MyContext} from "../../../../types"; + + +const test{{typeName}}{{capitalizedFieldName}} = (parent: ResolversParentTypes["{{typeName}}"], {{#if hasArguments}}variables: {{typeName}}{{capitalizedFieldName}}Args,{{/if}} context: MyContext) => {{typeName}}{{capitalizedFieldName}}(parent, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) + + +test("{{typeName}}{{capitalizedFieldName}}", async () => { + +const context = td.object(); + + // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() + + const parent: ResolversParentTypes["{{typeName}}"] = {} + {{#if hasArguments}} + const variables: {{typeName}}{{capitalizedFieldName}}Args = {} + {{/if}} + + const result = await test{{typeName}}{{capitalizedFieldName}}(parent, {{#if hasArguments}}variables,{{/if}} context); + + }); diff --git a/templates/types.handlebars b/templates/types.handlebars index 62e28b3..9107817 100644 --- a/templates/types.handlebars +++ b/templates/types.handlebars @@ -1,8 +1,4 @@ -/* eslint-disable import/no-cycle */ -// cycles are ok here since we only deal with types. -{{#each modules}}import { I{{name}}Context } from "./modules/{{name}}/I{{name}}Context"; -{{/each}} export interface MyContext { -{{#each modules}}{{name}}: I{{name}}Context;{{/each}} + } diff --git a/templates/useCase.handlebars b/templates/useCase.handlebars deleted file mode 100644 index 7ea875a..0000000 --- a/templates/useCase.handlebars +++ /dev/null @@ -1,12 +0,0 @@ -import { {{moduleName}}Repository} from "../repository/{{moduleName}}Repository"; -import {I{{moduleName}}Context} from "../I{{moduleName}}Context"; - -export const {{name}} = (repository: {{moduleName}}Repository) => { - -const internal: IListsContext["{{name}}"] = async ({{#if hasArguments}}{ {{#each variables}} {{this}}, {{/each}} }{{/if}}) => { -// Add your business logic here, everything else you can safely ignore, it's wired up for you! -return repository.doSomething(); -}; - -return internal; -} diff --git a/templates/useCase.spec.handlebars b/templates/useCase.spec.handlebars deleted file mode 100644 index 55839ac..0000000 --- a/templates/useCase.spec.handlebars +++ /dev/null @@ -1,20 +0,0 @@ -import td from "testdouble"; -{{#if hasArguments}}import { {{capitalizedName}}Args } from "../../../graphql/generated";{{/if}} -import { {{moduleName}}Repository} from "../repository/{{moduleName}}Repository"; -import { {{name}} } from "./{{name}}"; - -test("{{name}}", async () => { - const repository = td.object<{{moduleName}}Repository>(); - - const resolved: _REPLACE_ME_DbObject[] = []; - td.when(repository.findAll()).thenResolve(resolved); - - {{#if hasArguments}} - const args: {{capitalizedName}}Args = {} - {{/if}} - - const result = await {{name}}(repository)({{#if hasArguments}}args{{/if}}); - - expect(result).toEqual(resolved); -}); - From 65d6da7c10f0359efe6907984536a34709c477f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Wed, 24 Jun 2020 12:54:57 +0200 Subject: [PATCH 05/17] bumped version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e27da6f..0d4bb66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "7.1.0", + "version": "8.0.0-preview", "description": "", "main": "index.js", "bin": { From 616ba772d3fe50d13cb1b0019eab6998dadb91a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Thu, 25 Jun 2020 10:41:33 +0200 Subject: [PATCH 06/17] feat: make sure the module resolvers are properly defined in line with the schema --- package.json | 2 +- templates/moduleResolvers.handlebars | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0d4bb66..b52072d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "8.0.0-preview", + "version": "8.0.1-preview", "description": "", "main": "index.js", "bin": { diff --git a/templates/moduleResolvers.handlebars b/templates/moduleResolvers.handlebars index b73689d..7d486ee 100644 --- a/templates/moduleResolvers.handlebars +++ b/templates/moduleResolvers.handlebars @@ -1,3 +1,4 @@ +import {Resolvers} from "../../../graphql/generated"; {{#each queries}}import { {{name}}Query } from "./queries/{{name}}Query";{{/each}} {{#each mutations}}import { {{name}}Mutation } from "./mutations/{{name}}Mutation";{{/each}} {{#each typeResolvers}}{{#each fieldName}}import { {{../typeName}}{{capitalizedName}} } from "./types/{{../typeName}}{{capitalizedName}}";{{/each}}{{/each}} @@ -6,7 +7,7 @@ // Warning, this file is autogenerated, please don't change it manually. // Your changes will get overwritten. -export const {{moduleName}}Resolvers = { +export const {{moduleName}}Resolvers: Resolvers = { {{#each typeResolvers}} {{typeName}}: { {{#each fieldName}}{{name}}: {{../typeName}}{{capitalizedName}},{{/each}} }, {{/each}} {{#if queries.length}}Query: { {{#each queries}}{{name}}: {{name}}Query,{{/each}} },{{/if}} From 0353577c59b0a3e7f0ed111c461d3d06c74cb924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Fri, 26 Jun 2020 10:31:22 +0200 Subject: [PATCH 07/17] feat: base the type resolvers generation on the module schema - so things end up in the correct place --- generateModule.js | 13 ++++++++----- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/generateModule.js b/generateModule.js index 3d1b659..c5a0dd6 100755 --- a/generateModule.js +++ b/generateModule.js @@ -118,16 +118,19 @@ const createTypeResolvers = () => { modules.forEach(({ name, typeDefinitions, types, schemaString, queries, mutations }) => { let typeResolvers = []; if (types) { - // XXX TODO read this from the CLI - const typeDef = fs.readFileSync('./schema.graphql'); - - const source = new Source(typeDef); + let source = new Source(schemaString); let schema = buildSchema(source); shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/`); typeDefinitions.forEach((typeDef) => { let filtered = []; - const type = schema.getType(typeDef.name); + let type = schema.getType(typeDef.name); + if (!type) { + const newSchemaString = schemaString.replace(`extend type ${typeDef.name}`, `type ${typeDef.name}`) + let source = new Source(newSchemaString); + let schema = buildSchema(source); + type = schema.getType(typeDef.name); + } if (type.astNode) { filtered = type.astNode.fields.filter( (f) => diff --git a/package-lock.json b/package-lock.json index 5e6cb86..f4280ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "7.1.0", + "version": "8.0.2-preview", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b52072d..88424da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "8.0.1-preview", + "version": "8.0.2-preview", "description": "", "main": "index.js", "bin": { From 1fe291771997528a064101a4275d94733cd29d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Thu, 2 Jul 2020 10:03:44 +0200 Subject: [PATCH 08/17] fix: link fields should be generated as resolvers --- generateModule.js | 3 +-- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/generateModule.js b/generateModule.js index c5a0dd6..c0c1dce 100755 --- a/generateModule.js +++ b/generateModule.js @@ -138,8 +138,7 @@ const createTypeResolvers = () => { (d) => d.name.value === 'column' || d.name.value === 'id' || - d.name.value === 'embedded' || - d.name.value === 'link' + d.name.value === 'embedded' ) ); } diff --git a/package-lock.json b/package-lock.json index f4280ab..e219619 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "8.0.2-preview", + "version": "8.0.3-preview", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 88424da..2ab8bf8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "8.0.2-preview", + "version": "8.0.3-preview", "description": "", "main": "index.js", "bin": { From 9a75c314fa76ab3af85dab0824f6e7ec994e6210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Wed, 15 Jul 2020 14:26:21 +0200 Subject: [PATCH 09/17] feat: apollo federation support --- generateModule.js | 5 +++-- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/generateModule.js b/generateModule.js index c0c1dce..b2f6124 100755 --- a/generateModule.js +++ b/generateModule.js @@ -118,9 +118,9 @@ const createTypeResolvers = () => { modules.forEach(({ name, typeDefinitions, types, schemaString, queries, mutations }) => { let typeResolvers = []; if (types) { + schemaString = schemaString.replace(/extend type/g, `type`) let source = new Source(schemaString); let schema = buildSchema(source); - shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/`); typeDefinitions.forEach((typeDef) => { let filtered = []; @@ -138,7 +138,8 @@ const createTypeResolvers = () => { (d) => d.name.value === 'column' || d.name.value === 'id' || - d.name.value === 'embedded' + d.name.value === 'embedded' || + d.name.value === 'external' ) ); } diff --git a/package-lock.json b/package-lock.json index e219619..ff394f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "8.0.3-preview", + "version": "9.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2ab8bf8..1257ae8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "8.0.3-preview", + "version": "9.0.0", "description": "", "main": "index.js", "bin": { From d3c181f961339a30884bf929be124ff4b745d809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Wed, 15 Jul 2020 14:42:54 +0200 Subject: [PATCH 10/17] feat: further simplification of the generated code --- generateModule.js | 23 ++++++++++----------- package-lock.json | 2 +- package.json | 2 +- templates/context.handlebars | 21 +++++++++++++++++++ templates/mutation.spec.handlebars | 6 +++--- templates/query.spec.handlebars | 6 +++--- templates/root.handlebars | 8 +++++++ templates/startupConfig.handlebars | 7 ------- templates/typeTypeResolvers.spec.handlebars | 6 +++--- templates/types.handlebars | 4 ---- 10 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 templates/context.handlebars create mode 100644 templates/root.handlebars delete mode 100644 templates/startupConfig.handlebars delete mode 100644 templates/types.handlebars diff --git a/generateModule.js b/generateModule.js index b2f6124..956ac89 100755 --- a/generateModule.js +++ b/generateModule.js @@ -90,29 +90,28 @@ const createGlobalResolvers = () => { createGlobalResolvers(); -const createTypes = () => { - const templateName = './templates/types.handlebars'; - const context = { modules }; + +const createRoot = () => { + const templateName = './templates/root.handlebars'; const filePath = `${projectMainPath}/src/`; - const fileName = `types.ts`; + const fileName = `root.ts`; const keepIfExists = true; - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + saveRenderedTemplate(templateName, {}, filePath, fileName, keepIfExists); }; -createTypes(); +createRoot(); -const createStartupConfig = () => { - const templateName = './templates/startupConfig.handlebars'; - const context = { modules }; +const createContext = () => { + const templateName = './templates/context.handlebars'; const filePath = `${projectMainPath}/src/`; - const fileName = `startupConfig.ts`; + const fileName = `context.ts`; const keepIfExists = true; - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + saveRenderedTemplate(templateName, {}, filePath, fileName, keepIfExists); }; -createStartupConfig(); +createContext(); const createTypeResolvers = () => { modules.forEach(({ name, typeDefinitions, types, schemaString, queries, mutations }) => { diff --git a/package-lock.json b/package-lock.json index ff394f1..45b2824 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.0", + "version": "9.0.0-preview", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1257ae8..7892a11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.0", + "version": "9.0.0-preview", "description": "", "main": "index.js", "bin": { diff --git a/templates/context.handlebars b/templates/context.handlebars new file mode 100644 index 0000000..f5f0d88 --- /dev/null +++ b/templates/context.handlebars @@ -0,0 +1,21 @@ +import express from "express"; +import { RootInterface } from "./root"; + +export type GqlContext = RootInterface & { + headers: { + [key: string]: string | string[]; + }; + // jwt?: string; +}; + +export const appContext = (root: RootInterface) => ({ + req, +}: { + req: express.Request; +}): GqlContext => { + return { + ...root, + headers: req.headers, + // jwt: req.cookies.jwt, + }; +}; diff --git a/templates/mutation.spec.handlebars b/templates/mutation.spec.handlebars index a895de8..5daea76 100644 --- a/templates/mutation.spec.handlebars +++ b/templates/mutation.spec.handlebars @@ -1,14 +1,14 @@ import td from "testdouble"; {{#if hasArguments}}import { Mutation{{mutationName}}Args } from "../../../../graphql/generated";{{/if}} import { {{mutationName}}Mutation } from "./{{mutationName}}Mutation"; -import {MyContext} from "../../../../types"; +import { GqlContext } from "../../../../context"; -const test{{mutationName}} = ({{#if hasArguments}}variables: Mutation{{mutationName}}Args, {{/if}}context: MyContext) => {{mutationName}}Mutation({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) +const test{{mutationName}} = ({{#if hasArguments}}variables: Mutation{{mutationName}}Args, {{/if}}context: GqlContext) => {{mutationName}}Mutation({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) test("{{mutationName}}", async () => { -const context = td.object(); +const context = td.object(); // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() diff --git a/templates/query.spec.handlebars b/templates/query.spec.handlebars index 491bb9f..f74148d 100644 --- a/templates/query.spec.handlebars +++ b/templates/query.spec.handlebars @@ -1,15 +1,15 @@ import td from "testdouble"; {{#if hasArguments}}import { Query{{queryName}}Args } from "../../../../graphql/generated";{{/if}} import { {{queryName}}Query } from "./{{queryName}}Query"; -import {MyContext} from "../../../../types"; +import { GqlContext } from "../../../../context"; -const test{{queryName}} = ({{#if hasArguments}}variables: Query{{queryName}}Args,{{/if}} context: MyContext) => {{queryName}}Query({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) +const test{{queryName}} = ({{#if hasArguments}}variables: Query{{queryName}}Args,{{/if}} context: GqlContext) => {{queryName}}Query({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) test("{{queryName}}", async () => { - const context = td.object(); + const context = td.object(); // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() diff --git a/templates/root.handlebars b/templates/root.handlebars new file mode 100644 index 0000000..898a726 --- /dev/null +++ b/templates/root.handlebars @@ -0,0 +1,8 @@ +// Initialize your Controllers / Data Sources / Repositories here. +// Thie shape of this object will also be extended by your context.ts file to define a Gql Context + +export const root = { + // todoItemController: new TodoItemControllerCacheable(), +}; + +export type RootInterface = typeof root; diff --git a/templates/startupConfig.handlebars b/templates/startupConfig.handlebars deleted file mode 100644 index 586f2e0..0000000 --- a/templates/startupConfig.handlebars +++ /dev/null @@ -1,7 +0,0 @@ -import { MyContext } from "./types"; -import { schema } from "./graphql/schema"; - -const context = (): MyContext => ({ - -}); -export const startupConfig = { schema, context }; diff --git a/templates/typeTypeResolvers.spec.handlebars b/templates/typeTypeResolvers.spec.handlebars index 4dcf829..2ebd21b 100644 --- a/templates/typeTypeResolvers.spec.handlebars +++ b/templates/typeTypeResolvers.spec.handlebars @@ -2,15 +2,15 @@ import td from "testdouble"; import { ResolversParentTypes, {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "../../../../graphql/generated"; import { {{typeName}}{{capitalizedFieldName}} } from "./{{typeName}}{{capitalizedFieldName}}"; -import {MyContext} from "../../../../types"; +import {GqlContext} from "../../../../types"; -const test{{typeName}}{{capitalizedFieldName}} = (parent: ResolversParentTypes["{{typeName}}"], {{#if hasArguments}}variables: {{typeName}}{{capitalizedFieldName}}Args,{{/if}} context: MyContext) => {{typeName}}{{capitalizedFieldName}}(parent, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) +const test{{typeName}}{{capitalizedFieldName}} = (parent: ResolversParentTypes["{{typeName}}"], {{#if hasArguments}}variables: {{typeName}}{{capitalizedFieldName}}Args,{{/if}} context: GqlContext) => {{typeName}}{{capitalizedFieldName}}(parent, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) test("{{typeName}}{{capitalizedFieldName}}", async () => { -const context = td.object(); +const context = td.object(); // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() diff --git a/templates/types.handlebars b/templates/types.handlebars deleted file mode 100644 index 9107817..0000000 --- a/templates/types.handlebars +++ /dev/null @@ -1,4 +0,0 @@ - -export interface MyContext { - -} From d0eb058dff89eb65987080d5f7d96bb6d4e67d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Thu, 16 Jul 2020 19:33:36 +0200 Subject: [PATCH 11/17] further work with apollo --- generateModule.js | 40 +++++++++++++-------- parseGraphql/getFederatedEntities.js | 14 ++++++++ parseGraphql/getFederatedEntities.spec.js | 25 +++++++++++++ templates/typeTypeResolvers.handlebars | 2 +- templates/typeTypeResolvers.spec.handlebars | 10 +++--- 5 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 parseGraphql/getFederatedEntities.js create mode 100644 parseGraphql/getFederatedEntities.spec.js diff --git a/generateModule.js b/generateModule.js index 956ac89..fb345a1 100755 --- a/generateModule.js +++ b/generateModule.js @@ -6,6 +6,7 @@ const fs = require('fs'); const getModuleInfos = require('./parsegraphql/getModuleInfos'); const getModuleNames = require('./parsegraphql/getModuleNames'); +const getFederatedEntities = require('./parsegraphql/getFederatedEntities'); const checkIfGitStateClean = require('./helpers/checkIfGitStateClean'); const saveRenderedTemplate = require('./helpers/saveRenderedTemplate'); checkIfGitStateClean(); @@ -90,7 +91,6 @@ const createGlobalResolvers = () => { createGlobalResolvers(); - const createRoot = () => { const templateName = './templates/root.handlebars'; const filePath = `${projectMainPath}/src/`; @@ -117,7 +117,8 @@ const createTypeResolvers = () => { modules.forEach(({ name, typeDefinitions, types, schemaString, queries, mutations }) => { let typeResolvers = []; if (types) { - schemaString = schemaString.replace(/extend type/g, `type`) + const federatedEntities = getFederatedEntities(schemaString); + schemaString = schemaString.replace(/extend type/g, `type`); let source = new Source(schemaString); let schema = buildSchema(source); shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/`); @@ -125,32 +126,40 @@ const createTypeResolvers = () => { let filtered = []; let type = schema.getType(typeDef.name); if (!type) { - const newSchemaString = schemaString.replace(`extend type ${typeDef.name}`, `type ${typeDef.name}`) + const newSchemaString = schemaString.replace(`extend type ${typeDef.name}`, `type ${typeDef.name}`); let source = new Source(newSchemaString); let schema = buildSchema(source); type = schema.getType(typeDef.name); } if (type.astNode) { - filtered = type.astNode.fields.filter( - (f) => - !f.directives.find( - (d) => - d.name.value === 'column' || - d.name.value === 'id' || - d.name.value === 'embedded' || - d.name.value === 'external' - ) - ); + if (type.astNode.directives && !!type.astNode.directives.find((d) => d.name.value === 'entity')) { + filtered = type.astNode.fields.filter( + (f) => + !f.directives.find( + (d) => + d.name.value === 'column' || + d.name.value === 'id' || + d.name.value === 'embedded' || + d.name.value === 'external' + ) + ); + } else { + filtered = type.astNode.fields.filter((f) => f.directives.find((d) => d.name.value === 'computed')); + } } + if (federatedEntities.find((e) => e === typeDef.name)) { + filtered = filtered.concat(federatedEntities.map((e) => ({ name: { value: '__resolveReference' }, resolveReferenceType: true }))); + } - filtered.forEach(({ name: { value } }) => { + filtered.forEach(({ name: { value }, resolveReferenceType }) => { const templateName = './templates/typeTypeResolvers.handlebars'; let capitalizedFieldName = capitalize(value); const context = { typeName: typeDef.name, fieldName: value, moduleName: name, + resolveReferenceType, capitalizedFieldName, }; const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; @@ -160,7 +169,7 @@ const createTypeResolvers = () => { saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }); - filtered.forEach(({ name: { value }, arguments }) => { + filtered.forEach(({ name: { value }, arguments, resolveReferenceType }) => { const templateName = './templates/typeTypeResolvers.spec.handlebars'; let capitalizedFieldName = capitalize(value); const context = { @@ -168,6 +177,7 @@ const createTypeResolvers = () => { fieldName: value, moduleName: name, hasArguments: arguments && arguments.length, + resolveReferenceType, capitalizedFieldName, }; const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; diff --git a/parseGraphql/getFederatedEntities.js b/parseGraphql/getFederatedEntities.js new file mode 100644 index 0000000..90876cb --- /dev/null +++ b/parseGraphql/getFederatedEntities.js @@ -0,0 +1,14 @@ +const gql = require('graphql-tag'); +module.exports = (graphqlString) => { + const graphqlAST = gql` + ${graphqlString} + `; + + return graphqlAST.definitions + .filter((d) => ['Mutation', 'Query', 'Subscription'].indexOf(d.name.value) === -1) + .filter((d) => ['ObjectTypeDefinition'].indexOf(d.kind) > -1) + .filter((d) => { + return d.directives && d.directives.find((d) => d.name.value === 'key'); + }) + .map((f) => f.name.value); +}; diff --git a/parseGraphql/getFederatedEntities.spec.js b/parseGraphql/getFederatedEntities.spec.js new file mode 100644 index 0000000..a98c46e --- /dev/null +++ b/parseGraphql/getFederatedEntities.spec.js @@ -0,0 +1,25 @@ +const getExtendedTypes = require('./getFederatedEntities'); +const gql = (a) => a[0]; + +test.only('get the non extended types with apollo key annotation', () => { + const schemaString = gql` + type TodoItem @key(fields: "id") { + id: ID! + list: List + } + + extend type List { + id: ID! + todos: [TodoItem!]! + incompleteCount: Int! + } + + type InMemory { + id: ID! + } + `; + + const res = getExtendedTypes(schemaString); + + expect(res).toEqual(['TodoItem']); +}); diff --git a/templates/typeTypeResolvers.handlebars b/templates/typeTypeResolvers.handlebars index f158361..35e2d0e 100644 --- a/templates/typeTypeResolvers.handlebars +++ b/templates/typeTypeResolvers.handlebars @@ -1,3 +1,3 @@ import { {{typeName}}Resolvers } from "../../../../graphql/generated"; -export const {{typeName}}{{capitalizedFieldName}}: {{typeName}}Resolvers["{{fieldName}}"] = (parent, args, context) => { throw new Error("Not implemented yet"); } +export const {{typeName}}{{capitalizedFieldName}}: {{typeName}}Resolvers["{{fieldName}}"] = (parent, {{#unless resolveReferenceType}}args,{{/unless}} context) => { throw new Error("Not implemented yet"); } diff --git a/templates/typeTypeResolvers.spec.handlebars b/templates/typeTypeResolvers.spec.handlebars index 2ebd21b..70e9c50 100644 --- a/templates/typeTypeResolvers.spec.handlebars +++ b/templates/typeTypeResolvers.spec.handlebars @@ -1,11 +1,12 @@ import td from "testdouble"; -import { ResolversParentTypes, {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "../../../../graphql/generated"; +{{#unless resolveReferenceType}}import { ResolversParentTypes, {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "../../../../graphql/generated";{{/unless}} import { {{typeName}}{{capitalizedFieldName}} } from "./{{typeName}}{{capitalizedFieldName}}"; -import {GqlContext} from "../../../../types"; +import { GqlContext } from "../../../../context"; +{{#if resolveReferenceType}}type ParentType = Parameters[0];{{/if}} -const test{{typeName}}{{capitalizedFieldName}} = (parent: ResolversParentTypes["{{typeName}}"], {{#if hasArguments}}variables: {{typeName}}{{capitalizedFieldName}}Args,{{/if}} context: GqlContext) => {{typeName}}{{capitalizedFieldName}}(parent, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) + const test{{typeName}}{{capitalizedFieldName}} = (parent: {{#if resolveReferenceType}}ParentType{{else}}ResolversParentTypes["{{typeName}}"]{{/if}}, {{#if hasArguments}}variables: {{typeName}}{{capitalizedFieldName}}Args,{{/if}} context: GqlContext) => {{typeName}}{{capitalizedFieldName}}(parent, {{#unless resolveReferenceType}}{{#if hasArguments}}variables{{else}} {} {{/if}},{{/unless}} context, null) test("{{typeName}}{{capitalizedFieldName}}", async () => { @@ -14,7 +15,8 @@ const context = td.object(); // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() - const parent: ResolversParentTypes["{{typeName}}"] = {} + {{#unless resolveReferenceType}}args,{{/unless}} + const parent{{#if resolveReferenceType}} = {} as ParentType {{else}}: ResolversParentTypes["{{typeName}}"] = {}{{/if}} {{#if hasArguments}} const variables: {{typeName}}{{capitalizedFieldName}}Args = {} {{/if}} From d394b4093548da0950fb4a37c7f16153b7eb0a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Fri, 17 Jul 2020 12:32:35 +0200 Subject: [PATCH 12/17] more things moved to the generated folder --- generateModule.js | 54 ++++++++++++++++++++++-- index.js | 19 +++++++++ package-lock.json | 2 +- package.json | 6 +-- parseGraphql/getModuleInfos.spec.js | 2 +- templates/combineSchemas.ts | 47 +++++++++++++++++++++ templates/frameworkSchema.graphql | 8 ++++ templates/genericDataModelSchema.graphql | 14 ++++++ templates/moduleResolvers.handlebars | 9 ++-- templates/printSchema.ts | 4 ++ templates/resolvers.handlebars | 2 +- 11 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 templates/combineSchemas.ts create mode 100644 templates/frameworkSchema.graphql create mode 100644 templates/genericDataModelSchema.graphql create mode 100644 templates/printSchema.ts diff --git a/generateModule.js b/generateModule.js index fb345a1..07d6b8e 100755 --- a/generateModule.js +++ b/generateModule.js @@ -2,7 +2,6 @@ const finder = require('find-package-json'); const shelljs = require('shelljs'); const { Source, buildSchema } = require('graphql'); -const fs = require('fs'); const getModuleInfos = require('./parsegraphql/getModuleInfos'); const getModuleNames = require('./parsegraphql/getModuleNames'); @@ -22,6 +21,55 @@ const projectMainPath = f const graphqlPaths = shelljs.ls(`${projectMainPath}/src/modules/*/graphql/*.graphql`).map((p) => ({ name: p })); +shelljs.mkdir('-p', `${projectMainPath}/generated/graphql`); + +// "Framework" "generated" files - initial generation +const createCombineSchemas = () => { + const templateName = './templates/combineSchemas.ts'; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `combineSchemas.ts`; + + saveRenderedTemplate(templateName, {}, filePath, fileName); +}; + +createCombineSchemas(); + +const createPrintSchema = () => { + const templateName = './templates/printSchema.ts'; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `printSchema.ts`; + + saveRenderedTemplate(templateName, {}, filePath, fileName); +} + +createPrintSchema(); + +const createGenericDataModelSchema = () => { + const templateName = './templates/genericDataModelSchema.graphql'; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `genericDataModelSchema.graphql`; + + saveRenderedTemplate(templateName, {}, filePath, fileName); +} + +createGenericDataModelSchema(); + +const createFrameworkSchema = () => { + const templateName = './templates/frameworkSchema.graphql'; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `frameworkSchema.graphql`; + + saveRenderedTemplate(templateName, {}, filePath, fileName); +} + +createFrameworkSchema(); + +// End of "Framework" "generated" files + +// Initial App Setup files + +// + const moduleNames = getModuleNames(graphqlPaths); const modules = getModuleInfos(moduleNames); @@ -84,7 +132,7 @@ modules.forEach((module) => { const createGlobalResolvers = () => { const templateName = './templates/resolvers.handlebars'; const context = { modules }; - const filePath = `${projectMainPath}/src/graphql/`; + const filePath = `${projectMainPath}/generated/graphql/`; const fileName = `resolvers.ts`; saveRenderedTemplate(templateName, context, filePath, fileName); }; @@ -199,7 +247,7 @@ const createTypeResolvers = () => { const createModuleResolvers = () => { const templateName = './templates/moduleResolvers.handlebars'; const context = { moduleName, queries, mutations, typeResolvers }; - const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/`; + const filePath = `${projectMainPath}/generated/graphql/`; const fileName = `${moduleName}Resolvers.ts`; saveRenderedTemplate(templateName, context, filePath, fileName); }; diff --git a/index.js b/index.js index b083099..6888300 100755 --- a/index.js +++ b/index.js @@ -1,2 +1,21 @@ #!/usr/bin/env node +// const path = require('path'); +// const program = require('commander'); +// +// program +// .option('--postinstall [value]', 'path of your graphql schema file') +// .option('--destDirPath [value]', 'dir you want to store the generated queries') +// .option('--depthLimit [value]', 'query depth you want to limit(The default is 100)') +// .option('--typesPath [value]', 'path to your generated typescript file with GraphQL Types') +// .parse(process.argv); +// +// console.log(); +// const { schemaFilePath, destDirPath, typesPath, depthLimit = 100 } = program; +// +// const pathToDestDir = `${process.cwd()}${destDirPath}`; +// const pathToTypes = `${process.cwd()}${typesPath}`; +// const typesRelativePathWithExtension = path.relative(pathToDestDir, pathToTypes); +// const typesRelativePath = typesRelativePathWithExtension.replace(path.extname(typesRelativePathWithExtension), ''); + + require('./generateModule'); diff --git a/package-lock.json b/package-lock.json index 45b2824..f07eb2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.0-preview", + "version": "9.0.1-preview", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 7892a11..39315b8 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "gql-typescript-generator", - "version": "9.0.0-preview", + "version": "9.0.1-preview", "description": "", "main": "index.js", "bin": { "gqlg": "index.js" }, "scripts": { - "test": "mocha -u qunit", + "test": "jest", "lint": "./node_modules/.bin/eslint ./index.js" }, "repository": { @@ -19,7 +19,7 @@ "query", "generator" ], - "author": "timqian", + "author": "Lukasz Gandecki lukasz@xolv.io", "license": "MIT", "bugs": { "url": "https://github.com/TheBrainFamily/gql-typescript-generator/issues" diff --git a/parseGraphql/getModuleInfos.spec.js b/parseGraphql/getModuleInfos.spec.js index 2d775dc..e0c78fa 100644 --- a/parseGraphql/getModuleInfos.spec.js +++ b/parseGraphql/getModuleInfos.spec.js @@ -11,6 +11,6 @@ const exampleNames = [{ name: 'Accounts', graphqlFilePath: '/src/modules/Account test('', () => { const parsed = getModuleInfos(exampleNames)[0]; expect(parsed.name).toEqual('Accounts'); - expect(parsed.queries).toEqual([{ name: 'me' }]); + expect(parsed.queries).toMatchObject([{ name: 'me' }]); expect(parsed.types).toEqual(false); }); diff --git a/templates/combineSchemas.ts b/templates/combineSchemas.ts new file mode 100644 index 0000000..330d3f5 --- /dev/null +++ b/templates/combineSchemas.ts @@ -0,0 +1,47 @@ +import { loadTypedefsSync } from "@graphql-tools/load"; +import { + GraphQLFileLoader, + GraphQLFileLoaderOptions, +} from "@graphql-tools/graphql-file-loader"; +import { mergeTypeDefs } from "@graphql-tools/merge"; + +import shelljs from "shelljs"; + +const graphqlPaths = shelljs.ls( + `${__dirname}/../../src/modules/*/graphql/*.graphql` +); + +const mainSchema = `${__dirname}/../../src/graphql/schema.graphql`; +const allSchemas = graphqlPaths + .concat(mainSchema) + .concat(`${__dirname}/genericDataModelSchema.graphql`) + .concat(`${__dirname}/frameworkSchema.graphql`); + +class ExtendedGraphQLFileLoader extends GraphQLFileLoader { + handleFileContent( + rawSDL: string, + pointer: string, + options: GraphQLFileLoaderOptions + ) { + if (pointer.indexOf("schema.graphql") > -1) { + const newSdl = rawSDL + .replace("type Query", "") + .replace("type Mutation", ""); + return super.handleFileContent(newSdl, pointer, options); + } + const newSdl = rawSDL + .replace("extend type Query", "type Query") + .replace("extend type Mutation", "type Mutation"); + // .replace(/extend type/g, "type"); + return super.handleFileContent(newSdl, pointer, options); + } +} + +const schema = mergeTypeDefs( + loadTypedefsSync(allSchemas, { + loaders: [new ExtendedGraphQLFileLoader()], + assumeValidSDL: true, // this will bypass validation + }).map((s) => s.document) +); + +export default schema; diff --git a/templates/frameworkSchema.graphql b/templates/frameworkSchema.graphql new file mode 100644 index 0000000..dc67977 --- /dev/null +++ b/templates/frameworkSchema.graphql @@ -0,0 +1,8 @@ +## GraphQL Template +type Query +type Mutation + +schema { + query: Query + mutation: Mutation +} diff --git a/templates/genericDataModelSchema.graphql b/templates/genericDataModelSchema.graphql new file mode 100644 index 0000000..975443e --- /dev/null +++ b/templates/genericDataModelSchema.graphql @@ -0,0 +1,14 @@ +# Mongo types +directive @entity(embedded: Boolean) on OBJECT + +directive @column(overrideType: String) on FIELD_DEFINITION + +directive @id on FIELD_DEFINITION +directive @link(overrideType: String) on FIELD_DEFINITION +directive @embedded on FIELD_DEFINITION +directive @union(discriminatorField: String) on UNION + +input AdditionalEntityFields { + path: String + type: String +} diff --git a/templates/moduleResolvers.handlebars b/templates/moduleResolvers.handlebars index 7d486ee..e7affaf 100644 --- a/templates/moduleResolvers.handlebars +++ b/templates/moduleResolvers.handlebars @@ -1,8 +1,7 @@ -import {Resolvers} from "../../../graphql/generated"; -{{#each queries}}import { {{name}}Query } from "./queries/{{name}}Query";{{/each}} -{{#each mutations}}import { {{name}}Mutation } from "./mutations/{{name}}Mutation";{{/each}} -{{#each typeResolvers}}{{#each fieldName}}import { {{../typeName}}{{capitalizedName}} } from "./types/{{../typeName}}{{capitalizedName}}";{{/each}}{{/each}} -{{#if types}}import { typeResolvers } from "./types/typeResolvers";{{/if}} +import {Resolvers} from "./types"; +{{#each queries}}import { {{name}}Query } from "@app/modules/{{../moduleName}}/graphql/queries/{{name}}Query";{{/each}} +{{#each mutations}}import { {{name}}Mutation } from "@app/modules/{{../moduleName}}/graphql/mutations/{{name}}Mutation";{{/each}} +{{#each typeResolvers}}{{#each fieldName}}import { {{../typeName}}{{capitalizedName}} } from "@app/modules/{{../../moduleName}}/graphql/types/{{../typeName}}{{capitalizedName}}";{{/each}}{{/each}} // Warning, this file is autogenerated, please don't change it manually. // Your changes will get overwritten. diff --git a/templates/printSchema.ts b/templates/printSchema.ts new file mode 100644 index 0000000..a090f56 --- /dev/null +++ b/templates/printSchema.ts @@ -0,0 +1,4 @@ +import { print } from "graphql"; +import schema from "./combineSchemas"; + +console.log(print(schema)); diff --git a/templates/resolvers.handlebars b/templates/resolvers.handlebars index 7a0569c..3612e70 100644 --- a/templates/resolvers.handlebars +++ b/templates/resolvers.handlebars @@ -1,5 +1,5 @@ import merge from "lodash/merge"; -{{#each modules}}import { {{name}}Resolvers } from "../modules/{{name}}/graphql/{{name}}Resolvers";{{/each}} +{{#each modules}}import { {{name}}Resolvers } from "./{{name}}Resolvers";{{/each}} // This file is generated. If you want to add custom resolvers do following steps: // 1. create customResolvers.ts file From 19fd55c35d9d818d38b3fd7cc71ed954bbff3831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Fri, 17 Jul 2020 13:11:15 +0200 Subject: [PATCH 13/17] preparing for a starter --- package-lock.json | 2 +- package.json | 2 +- templates/combineSchemas.ts | 17 +++++++---------- templates/frameworkSchema.graphql | 5 ----- templates/mutation.handlebars | 2 +- templates/mutation.spec.handlebars | 3 +-- templates/query.handlebars | 2 +- templates/query.spec.handlebars | 3 +-- templates/typeTypeResolvers.handlebars | 2 +- templates/typeTypeResolvers.spec.handlebars | 3 +-- 10 files changed, 15 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index f07eb2b..b1842be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.1-preview", + "version": "9.0.1-preview-3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 39315b8..ccc1ccc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.1-preview", + "version": "9.0.1-preview-3", "description": "", "main": "index.js", "bin": { diff --git a/templates/combineSchemas.ts b/templates/combineSchemas.ts index 330d3f5..733aa10 100644 --- a/templates/combineSchemas.ts +++ b/templates/combineSchemas.ts @@ -1,3 +1,4 @@ +import * as fs from "fs"; import { loadTypedefsSync } from "@graphql-tools/load"; import { GraphQLFileLoader, @@ -11,11 +12,13 @@ const graphqlPaths = shelljs.ls( `${__dirname}/../../src/modules/*/graphql/*.graphql` ); -const mainSchema = `${__dirname}/../../src/graphql/schema.graphql`; -const allSchemas = graphqlPaths - .concat(mainSchema) +let allSchemas = graphqlPaths .concat(`${__dirname}/genericDataModelSchema.graphql`) - .concat(`${__dirname}/frameworkSchema.graphql`); + +const schemaExtensionPath = `${__dirname}/../../src/graphql/schema.graphql` +if (fs.existsSync(schemaExtensionPath)) { + allSchemas = allSchemas.concat(schemaExtensionPath) +} class ExtendedGraphQLFileLoader extends GraphQLFileLoader { handleFileContent( @@ -23,12 +26,6 @@ class ExtendedGraphQLFileLoader extends GraphQLFileLoader { pointer: string, options: GraphQLFileLoaderOptions ) { - if (pointer.indexOf("schema.graphql") > -1) { - const newSdl = rawSDL - .replace("type Query", "") - .replace("type Mutation", ""); - return super.handleFileContent(newSdl, pointer, options); - } const newSdl = rawSDL .replace("extend type Query", "type Query") .replace("extend type Mutation", "type Mutation"); diff --git a/templates/frameworkSchema.graphql b/templates/frameworkSchema.graphql index dc67977..bc1175e 100644 --- a/templates/frameworkSchema.graphql +++ b/templates/frameworkSchema.graphql @@ -1,8 +1,3 @@ ## GraphQL Template type Query type Mutation - -schema { - query: Query - mutation: Mutation -} diff --git a/templates/mutation.handlebars b/templates/mutation.handlebars index 7662864..cf07e9c 100644 --- a/templates/mutation.handlebars +++ b/templates/mutation.handlebars @@ -1,3 +1,3 @@ -import { MutationResolvers } from "../../../../graphql/generated"; +import { MutationResolvers } from "@generated/graphql/types"; export const {{mutationName}}Mutation: MutationResolvers["{{mutationName}}"] = (parent, args, context) => { throw new Error("not implemented yet"); } diff --git a/templates/mutation.spec.handlebars b/templates/mutation.spec.handlebars index 5daea76..3b279b7 100644 --- a/templates/mutation.spec.handlebars +++ b/templates/mutation.spec.handlebars @@ -1,7 +1,6 @@ import td from "testdouble"; -{{#if hasArguments}}import { Mutation{{mutationName}}Args } from "../../../../graphql/generated";{{/if}} +import { GqlContext, {{#if hasArguments}}Mutation{{mutationName}}Args{{/if}} } from "@generated/graphql/types"; import { {{mutationName}}Mutation } from "./{{mutationName}}Mutation"; -import { GqlContext } from "../../../../context"; const test{{mutationName}} = ({{#if hasArguments}}variables: Mutation{{mutationName}}Args, {{/if}}context: GqlContext) => {{mutationName}}Mutation({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) diff --git a/templates/query.handlebars b/templates/query.handlebars index bc8cc4f..efdd4e8 100644 --- a/templates/query.handlebars +++ b/templates/query.handlebars @@ -1,4 +1,4 @@ -import { QueryResolvers } from "../../../../graphql/generated"; +import { QueryResolvers } from "@generated/graphql/types"; export const {{queryName}}Query: QueryResolvers["{{queryName}}"] = (parent, args, context) => { throw new Error("not implemented yet"); } diff --git a/templates/query.spec.handlebars b/templates/query.spec.handlebars index f74148d..b9d3fef 100644 --- a/templates/query.spec.handlebars +++ b/templates/query.spec.handlebars @@ -1,7 +1,6 @@ import td from "testdouble"; -{{#if hasArguments}}import { Query{{queryName}}Args } from "../../../../graphql/generated";{{/if}} +import { GqlContext, {{#if hasArguments}}Query{{queryName}}Args{{/if}} } from "@generated/graphql/types"; import { {{queryName}}Query } from "./{{queryName}}Query"; -import { GqlContext } from "../../../../context"; const test{{queryName}} = ({{#if hasArguments}}variables: Query{{queryName}}Args,{{/if}} context: GqlContext) => {{queryName}}Query({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) diff --git a/templates/typeTypeResolvers.handlebars b/templates/typeTypeResolvers.handlebars index 35e2d0e..65491a7 100644 --- a/templates/typeTypeResolvers.handlebars +++ b/templates/typeTypeResolvers.handlebars @@ -1,3 +1,3 @@ -import { {{typeName}}Resolvers } from "../../../../graphql/generated"; +import { {{typeName}}Resolvers } from "@generated/graphql/types"; export const {{typeName}}{{capitalizedFieldName}}: {{typeName}}Resolvers["{{fieldName}}"] = (parent, {{#unless resolveReferenceType}}args,{{/unless}} context) => { throw new Error("Not implemented yet"); } diff --git a/templates/typeTypeResolvers.spec.handlebars b/templates/typeTypeResolvers.spec.handlebars index 70e9c50..5c4bd86 100644 --- a/templates/typeTypeResolvers.spec.handlebars +++ b/templates/typeTypeResolvers.spec.handlebars @@ -1,8 +1,7 @@ import td from "testdouble"; -{{#unless resolveReferenceType}}import { ResolversParentTypes, {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "../../../../graphql/generated";{{/unless}} +import { GqlContext, {{#if resolveReferenceType}}ResolversParentTypes, {{/if}} {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "@generated/graphql/types"; import { {{typeName}}{{capitalizedFieldName}} } from "./{{typeName}}{{capitalizedFieldName}}"; -import { GqlContext } from "../../../../context"; {{#if resolveReferenceType}}type ParentType = Parameters[0];{{/if}} From b98b3710a67989bede60325dde493f47f80c2622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Fri, 17 Jul 2020 14:03:01 +0200 Subject: [PATCH 14/17] scaffolding works --- .eslintrc | 4 - generateModule.js | 3 +- helpers/checkIfGitStateClean.js | 2 +- index.js | 49 +++-- package-lock.json | 192 ++---------------- package.json | 5 +- scaffold/.eslintrc | 32 +++ scaffold/.gitignore | 9 + scaffold/README.md | 64 ++++++ scaffold/codegen.js | 72 +++++++ scaffold/fix-generated.js | 8 + scaffold/jest.config.js | 13 ++ scaffold/jest.setup.js | 4 + scaffold/nodemon.run.json | 11 + scaffold/package.json | 80 ++++++++ scaffold/src/context.ts | 21 ++ scaffold/src/createApp.ts | 40 ++++ scaffold/src/graphql/schema.graphql.skip | 15 ++ scaffold/src/graphql/schema.ts | 12 ++ scaffold/src/index.ts | 25 +++ .../modules/RemoveMe/graphql/RemoveMe.graphql | 3 + .../graphql/queries/HelloQuery.spec.ts | 16 ++ .../RemoveMe/graphql/queries/HelloQuery.ts | 5 + scaffold/src/root.ts | 6 + scaffold/tsconfig.json | 18 ++ 25 files changed, 504 insertions(+), 205 deletions(-) create mode 100644 scaffold/.eslintrc create mode 100644 scaffold/.gitignore create mode 100644 scaffold/README.md create mode 100644 scaffold/codegen.js create mode 100644 scaffold/fix-generated.js create mode 100644 scaffold/jest.config.js create mode 100644 scaffold/jest.setup.js create mode 100644 scaffold/nodemon.run.json create mode 100644 scaffold/package.json create mode 100644 scaffold/src/context.ts create mode 100644 scaffold/src/createApp.ts create mode 100644 scaffold/src/graphql/schema.graphql.skip create mode 100644 scaffold/src/graphql/schema.ts create mode 100644 scaffold/src/index.ts create mode 100644 scaffold/src/modules/RemoveMe/graphql/RemoveMe.graphql create mode 100644 scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.spec.ts create mode 100644 scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.ts create mode 100644 scaffold/src/root.ts create mode 100644 scaffold/tsconfig.json diff --git a/.eslintrc b/.eslintrc index d6253f1..04889ec 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,9 +4,5 @@ "no-console": "off", "global-require": "off", "no-plusplus": "off" - }, - "env": { - "mocha": true } } - \ No newline at end of file diff --git a/generateModule.js b/generateModule.js index 07d6b8e..53cb0a3 100755 --- a/generateModule.js +++ b/generateModule.js @@ -6,9 +6,8 @@ const { Source, buildSchema } = require('graphql'); const getModuleInfos = require('./parsegraphql/getModuleInfos'); const getModuleNames = require('./parsegraphql/getModuleNames'); const getFederatedEntities = require('./parsegraphql/getFederatedEntities'); -const checkIfGitStateClean = require('./helpers/checkIfGitStateClean'); +// const checkIfGitStateClean = require('./helpers/checkIfGitStateClean'); const saveRenderedTemplate = require('./helpers/saveRenderedTemplate'); -checkIfGitStateClean(); const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1); diff --git a/helpers/checkIfGitStateClean.js b/helpers/checkIfGitStateClean.js index 4c0df54..5aefc93 100644 --- a/helpers/checkIfGitStateClean.js +++ b/helpers/checkIfGitStateClean.js @@ -32,7 +32,7 @@ module.exports = function checkIfGitStateClean() { const status = statusSync(currentPath); if (status.changed && !process.env.IGNORE_GIT) { - console.error(`We are about to generate and modify some code, but you have ${status.dirty} modified and ${status.untracked} files. We are counting only files in src/ and excluding .graphql. + console.error(`We are about to generate and modify some code, but you have ${status.dirty} modified and ${status.untracked} new files. We are counting only files in src/ and excluding .graphql. To make sure you can easily verify and revert the changes introduced by this tool, please commit or stash the existing changes. If you want to ignore this warning run the tooling with IGNORE_GIT=true. This is STRONGLY discouraged!`); process.exit(2); diff --git a/index.js b/index.js index 6888300..8ca3b2c 100755 --- a/index.js +++ b/index.js @@ -1,21 +1,34 @@ #!/usr/bin/env node -// const path = require('path'); -// const program = require('commander'); -// -// program -// .option('--postinstall [value]', 'path of your graphql schema file') -// .option('--destDirPath [value]', 'dir you want to store the generated queries') -// .option('--depthLimit [value]', 'query depth you want to limit(The default is 100)') -// .option('--typesPath [value]', 'path to your generated typescript file with GraphQL Types') -// .parse(process.argv); -// -// console.log(); -// const { schemaFilePath, destDirPath, typesPath, depthLimit = 100 } = program; -// -// const pathToDestDir = `${process.cwd()}${destDirPath}`; -// const pathToTypes = `${process.cwd()}${typesPath}`; -// const typesRelativePathWithExtension = path.relative(pathToDestDir, pathToTypes); -// const typesRelativePath = typesRelativePathWithExtension.replace(path.extname(typesRelativePathWithExtension), ''); +const fs = require("fs"); +const program = require('commander'); +const shelljs = require('shelljs'); -require('./generateModule'); +program + .option('--create ', 'Create a new app - pass a name') + .option('--codeGen', 'Generate code') + .parse(process.argv); + +const { create, codeGen } = program; + +const scaffoldDir = `${process.cwd()}/${create}`; + +if (create) { + if (fs.existsSync(scaffoldDir)) { + console.log(`Path: ${scaffoldDir} already exists. Can't create a new app in an already existing path.`) + process.exit(1) + } + shelljs.cp('-R', `${__dirname}/scaffold`, `${scaffoldDir}`) + shelljs.exec(`cd ${create} && git init .`) + console.log(`\n${create} created successfully!`) + console.log(`run: + cd ${create} + npm install`) + console.log('and start hacking! :-)') + process.exit(1) +} + + +if (codeGen) { + require("./generateModule") +} diff --git a/package-lock.json b/package-lock.json index b1842be..e0ed1e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.1-preview-3", + "version": "9.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1366,6 +1366,7 @@ "version": "1.0.2", "resolved": "http://registry.npm.taobao.org/array-union/download/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, "requires": { "array-uniq": "^1.0.1" } @@ -1373,7 +1374,8 @@ "array-uniq": { "version": "1.0.3", "resolved": "http://registry.npm.taobao.org/array-uniq/download/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true }, "array-unique": { "version": "0.3.2", @@ -1659,12 +1661,6 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "http://registry.npm.taobao.org/browser-stdout/download/browser-stdout-1.3.1.tgz", - "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=", - "dev": true - }, "bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -2019,15 +2015,6 @@ "whatwg-url": "^8.0.0" } }, - "debug": { - "version": "3.1.0", - "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -2108,19 +2095,6 @@ } } }, - "del": { - "version": "3.0.0", - "resolved": "http://registry.npm.taobao.org/del/download/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2133,12 +2107,6 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, - "diff": { - "version": "3.5.0", - "resolved": "http://registry.npm.taobao.org/diff/download/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", - "dev": true - }, "diff-sequences": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", @@ -2936,25 +2904,6 @@ "integrity": "sha1-we9F7pvta63wZjxcuQ6NGt7BMh0=", "dev": true }, - "globby": { - "version": "6.1.0", - "resolved": "http://registry.npm.taobao.org/globby/download/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "http://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, "graceful-fs": { "version": "4.1.11", "resolved": "http://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.1.11.tgz", @@ -2974,12 +2923,6 @@ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz", "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==" }, - "growl": { - "version": "1.10.3", - "resolved": "http://registry.npm.taobao.org/growl/download/growl-1.10.3.tgz", - "integrity": "sha1-GSa6kM8+3+KttJJ/WIC8IsZseQ8=", - "dev": true - }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -3043,12 +2986,6 @@ "function-bind": "^1.1.1" } }, - "has-flag": { - "version": "2.0.0", - "resolved": "http://registry.npm.taobao.org/has-flag/download/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, "has-symbols": { "version": "1.0.0", "resolved": "http://registry.npm.taobao.org/has-symbols/download/has-symbols-1.0.0.tgz", @@ -3107,12 +3044,6 @@ } } }, - "he": { - "version": "1.1.1", - "resolved": "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, "hosted-git-info": { "version": "2.7.1", "resolved": "http://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.7.1.tgz", @@ -3423,12 +3354,14 @@ "is-path-cwd": { "version": "1.0.0", "resolved": "http://registry.npm.taobao.org/is-path-cwd/download/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true }, "is-path-in-cwd": { "version": "1.0.1", "resolved": "http://registry.npm.taobao.org/is-path-in-cwd/download/is-path-in-cwd-1.0.1.tgz", "integrity": "sha1-WsSLNF72dTOb1sekipEhELJBz1I=", + "dev": true, "requires": { "is-path-inside": "^1.0.0" } @@ -3437,6 +3370,7 @@ "version": "1.0.1", "resolved": "http://registry.npm.taobao.org/is-path-inside/download/is-path-inside-1.0.1.tgz", "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, "requires": { "path-is-inside": "^1.0.1" } @@ -5469,32 +5403,6 @@ "minimist": "0.0.8" } }, - "mocha": { - "version": "5.0.5", - "resolved": "http://registry.npm.taobao.org/mocha/download/mocha-5.0.5.tgz", - "integrity": "sha1-4ijjOGuTh6RxAAemQfEnsAvkS1I=", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - }, - "dependencies": { - "commander": { - "version": "2.11.0", - "resolved": "http://registry.npm.taobao.org/commander/download/commander-2.11.0.tgz", - "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", - "dev": true - } - } - }, "ms": { "version": "2.0.0", "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz", @@ -5626,7 +5534,8 @@ "object-assign": { "version": "4.1.1", "resolved": "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-copy": { "version": "0.1.0", @@ -5790,11 +5699,6 @@ "p-limit": "^1.1.0" } }, - "p-map": { - "version": "1.2.0", - "resolved": "http://registry.npm.taobao.org/p-map/download/p-map-1.2.0.tgz", - "integrity": "sha1-5OlPMR6rvIYzoeeZCBZfyiYkG2s=" - }, "p-try": { "version": "1.0.0", "resolved": "http://registry.npm.taobao.org/p-try/download/p-try-1.0.0.tgz", @@ -5839,7 +5743,8 @@ "path-is-inside": { "version": "1.0.2", "resolved": "http://registry.npm.taobao.org/path-is-inside/download/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true }, "path-key": { "version": "2.0.1", @@ -5881,20 +5786,17 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "pify": { - "version": "3.0.0", - "resolved": "http://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, "pinkie": { "version": "2.0.4", "resolved": "http://registry.npm.taobao.org/pinkie/download/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "http://registry.npm.taobao.org/pinkie-promise/download/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -6302,6 +6204,7 @@ "version": "2.6.2", "resolved": "http://registry.npm.taobao.org/rimraf/download/rimraf-2.6.2.tgz", "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "dev": true, "requires": { "glob": "^7.0.5" } @@ -6574,60 +6477,6 @@ "dev": true, "optional": true }, - "should": { - "version": "13.2.1", - "resolved": "http://registry.npm.taobao.org/should/download/should-13.2.1.tgz", - "integrity": "sha1-hObr+7FFx54K5CMHsls/YtyvV04=", - "dev": true, - "requires": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "should-equal": { - "version": "2.0.0", - "resolved": "http://registry.npm.taobao.org/should-equal/download/should-equal-2.0.0.tgz", - "integrity": "sha1-YHLPgwRzYIZ+aOmLCdcRQ9BO4MM=", - "dev": true, - "requires": { - "should-type": "^1.4.0" - } - }, - "should-format": { - "version": "3.0.3", - "resolved": "http://registry.npm.taobao.org/should-format/download/should-format-3.0.3.tgz", - "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", - "dev": true, - "requires": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "should-type": { - "version": "1.4.0", - "resolved": "http://registry.npm.taobao.org/should-type/download/should-type-1.4.0.tgz", - "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", - "dev": true - }, - "should-type-adaptors": { - "version": "1.1.0", - "resolved": "http://registry.npm.taobao.org/should-type-adaptors/download/should-type-adaptors-1.1.0.tgz", - "integrity": "sha1-QB5/M7VTMDOUTVzYvytlAneS4no=", - "dev": true, - "requires": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "should-util": { - "version": "1.0.0", - "resolved": "http://registry.npm.taobao.org/should-util/download/should-util-1.0.0.tgz", - "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", - "dev": true - }, "signal-exit": { "version": "3.0.2", "resolved": "http://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.2.tgz", @@ -6999,15 +6848,6 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, - "supports-color": { - "version": "4.4.0", - "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-4.4.0.tgz", - "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - }, "supports-hyperlinks": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", diff --git a/package.json b/package.json index ccc1ccc..016e4ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.1-preview-3", + "version": "9.0.2", "description": "", "main": "index.js", "bin": { @@ -27,7 +27,6 @@ "homepage": "https://github.com/TheBrainFamily/gql-typescript-generator#readme", "dependencies": { "commander": "^2.15.1", - "del": "^3.0.0", "find-package-json": "^1.2.0", "git-state": "^4.1.0", "graphql": "^0.13.2", @@ -40,9 +39,7 @@ "eslint-config-airbnb-base": "^13.1.0", "eslint-plugin-import": "^2.14.0", "jest": "^26.0.1", - "mocha": "^5.0.5", "prettier": "^2.0.5", - "should": "^13.2.1", "testdouble": "^3.15.0", "testdouble-jest": "^2.0.0" } diff --git a/scaffold/.eslintrc b/scaffold/.eslintrc new file mode 100644 index 0000000..50f173e --- /dev/null +++ b/scaffold/.eslintrc @@ -0,0 +1,32 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json" + }, + "extends": [ + "airbnb-typescript/base", + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier/@typescript-eslint", + "plugin:prettier/recommended" + ], + "plugins": ["@typescript-eslint", "prettier"], + "rules": { + "import/no-extraneous-dependencies": [ + "error", + { "devDependencies": ["**/*.test.ts", "**/*.spec.ts"] } + ], + "prettier/prettier": "error", + "import/prefer-default-export": 0, + "no-underscore-dangle": 0, + "no-cond-assign": ["error", "except-parens"], + "import/no-cycle": 0, + "@typescript-eslint/interface-name-prefix": 0, + "@typescript-eslint/camelcase": ["error",{"allow": ["resolveReference"]}] + }, + "env": { + "browser": true, + "jest": true + } +} diff --git a/scaffold/.gitignore b/scaffold/.gitignore new file mode 100644 index 0000000..d5dfb56 --- /dev/null +++ b/scaffold/.gitignore @@ -0,0 +1,9 @@ +.idea +.eslintcache +node_modules +coverage +lib/ +dist/ +.cache +generated/graphql +schema.graphql diff --git a/scaffold/README.md b/scaffold/README.md new file mode 100644 index 0000000..736708c --- /dev/null +++ b/scaffold/README.md @@ -0,0 +1,64 @@ +## Setup + +`npm install` + + +## Development + +Start the backend in dev (watch) mode: + +`npm start` + +To run tests: + +`npm test` + +To run tests in watch mode: + +`npm run test:watch` + +To run type check: + +`npm run type-check` + +To run lint: + +`npm run lint` + +## Type Generation / Workflow + +Your schema files have to be in a structure of `./src/modules/MODULE_NAME/graphql/MODULE_NAME.graphql`, for example `./src/modules/Lists/graphql/Lists.graphql`. + +Anytime you modify one of your graphql files remember to run `npm run graphql:generateAll`. + +It will create Mutations/Queries/Type resolvers, tests for them, types and perform all the necessary connection between the main schema, contexts, etc. +It's advisable to create a new module, fill it with schema, run the generation to see what are the resulting changes. +Remember to start with a clean, commited state. It's difficult to compare and verify the generation results if you've had changes in the code already, so that's blocked by default. +The tooling will only allow you to run the generation if there are no changes, or if .graphql files are the only one changed. + +Let's assume we've created a new module named Users: + +`mkdir -p src/modules/Users/graphql` + +And now create a simple schema file for it `src/modules/Users/graphql/Users.graphql`: + +```graphql +type User { + id: ID! + name: String +} + +extend type Query { + UserById(id: ID!): User! +} + +extend type Mutation { + UserAdd(name: String!): User! +} +``` + +> Please note, we extend Queries and Mutations, as those are defined something else - that helps IDEs to understand that we don't have conflicting types defined in our project + +Let's run the generation now: + +`npm run graphql:generateAll` diff --git a/scaffold/codegen.js b/scaffold/codegen.js new file mode 100644 index 0000000..612e6d2 --- /dev/null +++ b/scaffold/codegen.js @@ -0,0 +1,72 @@ +const fs = require("fs"); +const { importSchema } = require("graphql-import"); +const { isObjectType } = require("graphql"); + +let schemaString = fs + .readFileSync("./schema.graphql") + .toString() + .replace(/extend type/g, `type`); + +schemaString = `${schemaString} + +## Federation Types + +scalar _FieldSet + +directive @external on FIELD_DEFINITION +directive @requires(fields: _FieldSet!) on FIELD_DEFINITION +directive @provides(fields: _FieldSet!) on FIELD_DEFINITION +directive @key(fields: _FieldSet!) on OBJECT | INTERFACE + +directive @extends on OBJECT +` + .replace( + "@entity(embedded: Boolean)", + "@entity(embedded: Boolean, additionalFields: [AdditionalEntityFields])" + ) + .replace( + "@union(discriminatorField: String)", + "@union(discriminatorField: String, additionalFields: [AdditionalEntityFields])" + ); + +const schema = importSchema(schemaString, {}, { out: "GraphQLSchema" }); +const typeMap = schema.getTypeMap(); + +const mappers = {}; +for (const typeName in typeMap) { + const type = schema.getType(typeName); + if (isObjectType(type)) { + if (type.toConfig().astNode) { + if ( + type + .toConfig() + .astNode.directives.find((d) => d.name.value === "entity") + ) { + mappers[typeName] = `${typeName}DbObject`; + } + } + } +} + +module.exports = { + overwrite: true, + schema: schemaString, + generates: { + "generated/graphql/types.ts": { + config: { + contextType: "@app/context#GqlContext", + idFieldName: "id", + objectIdType: "string", + federation: true, + mappers, + }, + plugins: [ + "typescript", + "typescript-resolvers", + "typescript-operations", + "typescript-mongodb", + { add: "export {GqlContext};" }, + ], + }, + }, +}; diff --git a/scaffold/fix-generated.js b/scaffold/fix-generated.js new file mode 100644 index 0000000..afaa2e6 --- /dev/null +++ b/scaffold/fix-generated.js @@ -0,0 +1,8 @@ +const shell = require("shelljs"); + +shell.sed( + "-i", + /\| StitchingResolver;/, + "", + "./generated/graphql/types.ts" +); diff --git a/scaffold/jest.config.js b/scaffold/jest.config.js new file mode 100644 index 0000000..9f08989 --- /dev/null +++ b/scaffold/jest.config.js @@ -0,0 +1,13 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + testRunner: "jest-circus/runner", + setupFiles: ["./jest.setup.js"], + testPathIgnorePatterns: ["/node_modules/", "/.yalc/"], + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { + prefix: "/", + }), +}; diff --git a/scaffold/jest.setup.js b/scaffold/jest.setup.js new file mode 100644 index 0000000..e4aba47 --- /dev/null +++ b/scaffold/jest.setup.js @@ -0,0 +1,4 @@ +const td = require('testdouble') +require('testdouble-jest')(td, jest) + +process.env.TEST_ENV = "true" diff --git a/scaffold/nodemon.run.json b/scaffold/nodemon.run.json new file mode 100644 index 0000000..30d588d --- /dev/null +++ b/scaffold/nodemon.run.json @@ -0,0 +1,11 @@ +{ + "delay": 1500, + "watch": [ + "src" + ], + "ignore": [ + "src/**/*.spec.ts", + "src/**/*.test.ts" + ], + "ext": "ts,graphql" +} diff --git a/scaffold/package.json b/scaffold/package.json new file mode 100644 index 0000000..027300c --- /dev/null +++ b/scaffold/package.json @@ -0,0 +1,80 @@ +{ + "name": "graphql-todo-lists", + "version": "1.0.0", + "description": "Small Standalone Scaffold", + "license": "ISC", + "scripts": { + "graphql:codegen": "gqlg --codeGen", + "graphql:generateAll": "npm run graphql:codegen && npm run graphql:generateSchema && npm run graphql:typegen && npm run prettify:all", + "graphql:generateSchema": "ts-node -r tsconfig-paths/register ./generated/graphql/printSchema.ts > ./schema.graphql", + "graphql:typegen": "graphql-codegen --config codegen.js && node ./fix-generated", + "postinstall": "npm run graphql:generateAll", + "lint": "eslint --fix --ext .ts --quiet --cache src/", + "precommit": "lint-staged", + "prettify:all": "npx prettier --write \"src/**/*.ts\" \"generated/**/*.ts\" --loglevel error", + "start": "npm run start:ts", + "start:ts": "nodemon -r tsconfig-paths/register --config ./nodemon.run.json ./src/index.ts", + "test": "jest --forceExit", + "test:watch": "jest --watch", + "type-check": "tsc --noEmit", + "type-check:watch": "npm run type-check -- --watch" + }, + "lint-staged": { + "src/**/*.ts*": [ + "eslint --fix", + "jest --findRelatedTests --forceExit" + ], + "package.json": [ + "sort-package-json" + ] + }, + "dependencies": { + "@apollo/federation": "0.17.0", + "@graphql-tools/graphql-file-loader": "6.0.14", + "@graphql-tools/load": "6.0.14", + "@graphql-tools/merge": "6.0.14", + "apollo-server-express": "2.15.1", + "cache-manager": "3.3.0", + "cookie-parser": "1.4.5", + "express": "4.17.1", + "graphql": "15.3.0", + "jsonwebtoken": "8.5.1", + "lodash": "4.17.19", + "portable-fetch": "3.0.0", + "shelljs": "0.8.4", + "tsconfig-paths": "3.9.0" + }, + "devDependencies": { + "@graphql-codegen/add": "1.17.0", + "@graphql-codegen/cli": "1.17.0", + "@graphql-codegen/typescript": "1.17.0", + "@graphql-codegen/typescript-mongodb": "1.17.0", + "@graphql-codegen/typescript-operations": "1.17.0", + "@graphql-codegen/typescript-resolvers": "1.17.0", + "@jest/globals": "26.1.0", + "@types/jest": "26.0.4", + "@types/node": "14.0.23", + "@types/shelljs": "0.8.8", + "@typescript-eslint/eslint-plugin": "3.6.1", + "@typescript-eslint/parser": "3.6.1", + "eslint": "7.4.0", + "eslint-config-airbnb-typescript": "8.0.2", + "eslint-config-prettier": "6.11.0", + "eslint-plugin-import": "2.22.0", + "eslint-plugin-prettier": "3.1.4", + "gql-typescript-generator": "9.0.1-preview-3", + "graphql-import": "1.0.2", + "jest": "26.1.0", + "jest-circus": "26.1.0", + "kill-port": "1.6.1", + "lint-staged": "10.2.11", + "nodemon": "2.0.4", + "prettier": "2.0.5", + "sort-package-json": "1.44.0", + "testdouble": "3.16.1", + "testdouble-jest": "2.0.0", + "ts-jest": "26.1.3", + "ts-node": "8.10.2", + "typescript": "3.9.7" + } +} diff --git a/scaffold/src/context.ts b/scaffold/src/context.ts new file mode 100644 index 0000000..0cc1f42 --- /dev/null +++ b/scaffold/src/context.ts @@ -0,0 +1,21 @@ +import express from "express"; +import { RootInterface } from "./root"; + +export type GqlContext = RootInterface & { + headers: { + [key: string]: string | string[]; + }; + jwt?: string; +}; + +export const appContext = (root: RootInterface) => ({ + req, +}: { + req: express.Request; +}): GqlContext => { + return { + ...root, + headers: req.headers, + jwt: req.cookies.jwt, + }; +}; diff --git a/scaffold/src/createApp.ts b/scaffold/src/createApp.ts new file mode 100644 index 0000000..cb54a16 --- /dev/null +++ b/scaffold/src/createApp.ts @@ -0,0 +1,40 @@ +import { ApolloServer } from "apollo-server-express"; +import express from "express"; +import cookieParser from "cookie-parser"; + +import { appContext } from "./context"; +import { schema } from "./graphql/schema"; +import { root } from "./root"; + +// We are keeping async here because we want to be able to await operations without changing the API of createApp. +export const createApp = async () => { + const app = express(); + + app.use([cookieParser()]); + + // FIXES CORS ERROR - + // so if you need to block other domains, you have an example + const whitelist = [ + "http://localhost:3000", + "http://localhost:4000", + "http://xolv.io", + "https://xolv.io", + ]; + + const corsOptions = { + origin(origin, callback) { + const originIsWhitelisted = whitelist.indexOf(origin) !== -1 || !origin; + callback(null, originIsWhitelisted); + }, + credentials: true, + }; + + const apollo = new ApolloServer({ + schema, + context: appContext(root), + }); + + apollo.applyMiddleware({ app, cors: corsOptions }); + + return { app }; +}; diff --git a/scaffold/src/graphql/schema.graphql.skip b/scaffold/src/graphql/schema.graphql.skip new file mode 100644 index 0000000..12777f6 --- /dev/null +++ b/scaffold/src/graphql/schema.graphql.skip @@ -0,0 +1,15 @@ +## If you need to add extra generic directives here, change this file name to schema.graphql. + +# some common examples of directives: + +#directive @adminOnly on OBJECT | FIELD_DEFINITION +#directive @authorized(requires: [Roles]) on OBJECT | FIELD_DEFINITION + +#enum Roles { +# ADMIN +# REGISTERED_USER +#} + +type Status { + status: String! +} diff --git a/scaffold/src/graphql/schema.ts b/scaffold/src/graphql/schema.ts new file mode 100644 index 0000000..cd50a35 --- /dev/null +++ b/scaffold/src/graphql/schema.ts @@ -0,0 +1,12 @@ +import { buildFederatedSchema } from "@apollo/federation"; +import { resolvers } from "@generated/graphql/resolvers"; +import typeDefs from "@generated/graphql/combineSchemas"; + +const schema = buildFederatedSchema([ + { + typeDefs, + resolvers, + }, +]); + +export { schema }; diff --git a/scaffold/src/index.ts b/scaffold/src/index.ts new file mode 100644 index 0000000..74fa47c --- /dev/null +++ b/scaffold/src/index.ts @@ -0,0 +1,25 @@ +import { AddressInfo } from "net"; +import { createApp } from "./createApp"; + +const opts = { + port: process.env.PORT || 4002, +}; + +const getUrl = (address, port) => + `://${address === "::" ? "localhost" : address}:${port}`; + +createApp() + .then(({ app }) => { + const server = app.listen(opts.port); + const { port, address } = server.address() as AddressInfo; + process.once("SIGTERM", function () { + server.close(function () { + process.kill(process.pid, "SIGTERM"); + }); + }); + console.log(`Server listening on ${getUrl(address, port)}`); + console.log(`GraphQL playground at http://${getUrl(address, port)}/graphql`); + }) + .catch((e) => { + console.log("Server did not start up with this error", e); + }); diff --git a/scaffold/src/modules/RemoveMe/graphql/RemoveMe.graphql b/scaffold/src/modules/RemoveMe/graphql/RemoveMe.graphql new file mode 100644 index 0000000..a358d6d --- /dev/null +++ b/scaffold/src/modules/RemoveMe/graphql/RemoveMe.graphql @@ -0,0 +1,3 @@ +extend type Query { + Hello(greeting: String!): String +} diff --git a/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.spec.ts b/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.spec.ts new file mode 100644 index 0000000..1ece12a --- /dev/null +++ b/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.spec.ts @@ -0,0 +1,16 @@ +import td from "testdouble"; +import { GqlContext, QueryHelloArgs } from "@generated/graphql/types"; +import { HelloQuery } from "./HelloQuery"; + +const testHello = (variables: QueryHelloArgs, context: GqlContext) => + HelloQuery({}, variables, context, null); + +test("Hello", async () => { + const context = td.object(); + + const variables: QueryHelloArgs = { greeting: "Hello there!" }; + + const result = await testHello(variables, context); + + expect(result).toEqual("Hello there!"); +}); diff --git a/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.ts b/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.ts new file mode 100644 index 0000000..f77bcd6 --- /dev/null +++ b/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.ts @@ -0,0 +1,5 @@ +import { QueryResolvers } from "@generated/graphql/types"; + +export const HelloQuery: QueryResolvers["Hello"] = (parent, args, context) => { + return args.greeting; +}; diff --git a/scaffold/src/root.ts b/scaffold/src/root.ts new file mode 100644 index 0000000..137215d --- /dev/null +++ b/scaffold/src/root.ts @@ -0,0 +1,6 @@ +// Initialize your Controllers / Data Sources / Repositories here. +// Thie shape of this object will also be extended by your context.ts file to define a Gql Context + +export const root = {}; + +export type RootInterface = typeof root; diff --git a/scaffold/tsconfig.json b/scaffold/tsconfig.json new file mode 100644 index 0000000..870cdfe --- /dev/null +++ b/scaffold/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "module": "commonjs", + "target": "es2019", + "sourceMap": true, + "outDir": "./lib/", + "esModuleInterop": true, + "baseUrl": "./", + "paths": { + "@app/*": ["./src/*"], + "@generated/*": ["./generated/*"] + } + }, + "exclude": [ + "./node_modules" + ] +} From 970f089c2ac62215542188c1fd7a30b2e71aa890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Fri, 17 Jul 2020 14:03:47 +0200 Subject: [PATCH 15/17] always use the newest version of the generator in the scaffold --- package-lock.json | 2 +- package.json | 2 +- scaffold/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0ed1e5..736e035 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.2", + "version": "9.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 016e4ae..64ceeb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.2", + "version": "9.0.3", "description": "", "main": "index.js", "bin": { diff --git a/scaffold/package.json b/scaffold/package.json index 027300c..ce5c148 100644 --- a/scaffold/package.json +++ b/scaffold/package.json @@ -62,7 +62,7 @@ "eslint-config-prettier": "6.11.0", "eslint-plugin-import": "2.22.0", "eslint-plugin-prettier": "3.1.4", - "gql-typescript-generator": "9.0.1-preview-3", + "gql-typescript-generator": "latest", "graphql-import": "1.0.2", "jest": "26.1.0", "jest-circus": "26.1.0", From 124127997c1858ea5e2920144e21be34f320600a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Tue, 28 Jul 2020 14:06:47 +0200 Subject: [PATCH 16/17] fixed failure on Linux --- generateModule.js | 6 +++--- package.json | 2 +- {parseGraphql => parse-graphql}/getFederatedEntities.js | 0 .../getFederatedEntities.spec.js | 0 {parseGraphql => parse-graphql}/getModuleInfos.js | 0 {parseGraphql => parse-graphql}/getModuleInfos.spec.js | 0 {parseGraphql => parse-graphql}/getModuleNames.js | 0 {parseGraphql => parse-graphql}/getModuleNames.spec.js | 0 {parseGraphql => parse-graphql}/parseGraphql.js | 0 {parseGraphql => parse-graphql}/parseGraphql.spec.js | 0 10 files changed, 4 insertions(+), 4 deletions(-) rename {parseGraphql => parse-graphql}/getFederatedEntities.js (100%) rename {parseGraphql => parse-graphql}/getFederatedEntities.spec.js (100%) rename {parseGraphql => parse-graphql}/getModuleInfos.js (100%) rename {parseGraphql => parse-graphql}/getModuleInfos.spec.js (100%) rename {parseGraphql => parse-graphql}/getModuleNames.js (100%) rename {parseGraphql => parse-graphql}/getModuleNames.spec.js (100%) rename {parseGraphql => parse-graphql}/parseGraphql.js (100%) rename {parseGraphql => parse-graphql}/parseGraphql.spec.js (100%) diff --git a/generateModule.js b/generateModule.js index 53cb0a3..1fac8c5 100755 --- a/generateModule.js +++ b/generateModule.js @@ -3,9 +3,9 @@ const finder = require('find-package-json'); const shelljs = require('shelljs'); const { Source, buildSchema } = require('graphql'); -const getModuleInfos = require('./parsegraphql/getModuleInfos'); -const getModuleNames = require('./parsegraphql/getModuleNames'); -const getFederatedEntities = require('./parsegraphql/getFederatedEntities'); +const getModuleInfos = require('./parse-graphql/getModuleInfos'); +const getModuleNames = require('./parse-graphql/getModuleNames'); +const getFederatedEntities = require('./parse-graphql/getFederatedEntities'); // const checkIfGitStateClean = require('./helpers/checkIfGitStateClean'); const saveRenderedTemplate = require('./helpers/saveRenderedTemplate'); diff --git a/package.json b/package.json index 64ceeb0..e201b68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.3", + "version": "9.0.4", "description": "", "main": "index.js", "bin": { diff --git a/parseGraphql/getFederatedEntities.js b/parse-graphql/getFederatedEntities.js similarity index 100% rename from parseGraphql/getFederatedEntities.js rename to parse-graphql/getFederatedEntities.js diff --git a/parseGraphql/getFederatedEntities.spec.js b/parse-graphql/getFederatedEntities.spec.js similarity index 100% rename from parseGraphql/getFederatedEntities.spec.js rename to parse-graphql/getFederatedEntities.spec.js diff --git a/parseGraphql/getModuleInfos.js b/parse-graphql/getModuleInfos.js similarity index 100% rename from parseGraphql/getModuleInfos.js rename to parse-graphql/getModuleInfos.js diff --git a/parseGraphql/getModuleInfos.spec.js b/parse-graphql/getModuleInfos.spec.js similarity index 100% rename from parseGraphql/getModuleInfos.spec.js rename to parse-graphql/getModuleInfos.spec.js diff --git a/parseGraphql/getModuleNames.js b/parse-graphql/getModuleNames.js similarity index 100% rename from parseGraphql/getModuleNames.js rename to parse-graphql/getModuleNames.js diff --git a/parseGraphql/getModuleNames.spec.js b/parse-graphql/getModuleNames.spec.js similarity index 100% rename from parseGraphql/getModuleNames.spec.js rename to parse-graphql/getModuleNames.spec.js diff --git a/parseGraphql/parseGraphql.js b/parse-graphql/parseGraphql.js similarity index 100% rename from parseGraphql/parseGraphql.js rename to parse-graphql/parseGraphql.js diff --git a/parseGraphql/parseGraphql.spec.js b/parse-graphql/parseGraphql.spec.js similarity index 100% rename from parseGraphql/parseGraphql.spec.js rename to parse-graphql/parseGraphql.spec.js From 6e7307153b3f349d2642251b2682939afd79457e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Gandecki?= <4002543+lgandecki@users.noreply.github.com> Date: Tue, 28 Jul 2020 17:54:33 +0200 Subject: [PATCH 17/17] fixed a bug with typeTypeResolvers, allow for having lowercase queries/mutations --- helpers/saveRenderedTemplate.js | 4 ++++ package-lock.json | 2 +- package.json | 2 +- templates/mutation.spec.handlebars | 6 +++--- templates/query.spec.handlebars | 6 +++--- templates/typeTypeResolvers.spec.handlebars | 3 +-- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/helpers/saveRenderedTemplate.js b/helpers/saveRenderedTemplate.js index c96dc11..5fb680a 100644 --- a/helpers/saveRenderedTemplate.js +++ b/helpers/saveRenderedTemplate.js @@ -2,6 +2,10 @@ const fs = require('fs'); const Handlebars = require('handlebars'); const path = require('path'); +Handlebars.registerHelper('toUpperCase', function(str) { + return str.replace(/^\w/, c => c.toUpperCase()); +}); + module.exports = function saveRenderedTemplate (templateName, context, filePath, fileName, keepIfExists = false) { const combinedPath = path.join(filePath, fileName); if (keepIfExists && fs.existsSync(combinedPath)) { diff --git a/package-lock.json b/package-lock.json index 736e035..3aafc7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.3", + "version": "9.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e201b68..a11b2cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gql-typescript-generator", - "version": "9.0.4", + "version": "9.0.5", "description": "", "main": "index.js", "bin": { diff --git a/templates/mutation.spec.handlebars b/templates/mutation.spec.handlebars index 3b279b7..bae3b60 100644 --- a/templates/mutation.spec.handlebars +++ b/templates/mutation.spec.handlebars @@ -1,8 +1,8 @@ import td from "testdouble"; -import { GqlContext, {{#if hasArguments}}Mutation{{mutationName}}Args{{/if}} } from "@generated/graphql/types"; +import { GqlContext, {{#if hasArguments}}Mutation{{toUpperCase mutationName}}Args{{/if}} } from "@generated/graphql/types"; import { {{mutationName}}Mutation } from "./{{mutationName}}Mutation"; -const test{{mutationName}} = ({{#if hasArguments}}variables: Mutation{{mutationName}}Args, {{/if}}context: GqlContext) => {{mutationName}}Mutation({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) +const test{{mutationName}} = ({{#if hasArguments}}variables: Mutation{{toUpperCase mutationName}}Args, {{/if}}context: GqlContext) => {{mutationName}}Mutation({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) test("{{mutationName}}", async () => { @@ -12,7 +12,7 @@ const context = td.object(); // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() {{#if hasArguments}} - const variables: Mutation{{mutationName}}Args = {} + const variables: Mutation{{toUpperCase mutationName}}Args = {} {{/if}} const result = await test{{mutationName}}({{#if hasArguments}}variables,{{/if}} context); diff --git a/templates/query.spec.handlebars b/templates/query.spec.handlebars index b9d3fef..ad7cd92 100644 --- a/templates/query.spec.handlebars +++ b/templates/query.spec.handlebars @@ -1,9 +1,9 @@ import td from "testdouble"; -import { GqlContext, {{#if hasArguments}}Query{{queryName}}Args{{/if}} } from "@generated/graphql/types"; +import { GqlContext, {{#if hasArguments}}Query{{toUpperCase queryName}}Args{{/if}} } from "@generated/graphql/types"; import { {{queryName}}Query } from "./{{queryName}}Query"; -const test{{queryName}} = ({{#if hasArguments}}variables: Query{{queryName}}Args,{{/if}} context: GqlContext) => {{queryName}}Query({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) +const test{{queryName}} = ({{#if hasArguments}}variables: Query{{toUpperCase queryName}}Args,{{/if}} context: GqlContext) => {{queryName}}Query({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) test("{{queryName}}", async () => { @@ -13,7 +13,7 @@ test("{{queryName}}", async () => { // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() {{#if hasArguments}} - const variables: Query{{queryName}}Args = {} + const variables: Query{{toUpperCase queryName}}Args = {} {{/if}} const result = await test{{queryName}}({{#if hasArguments}}variables,{{/if}} context); diff --git a/templates/typeTypeResolvers.spec.handlebars b/templates/typeTypeResolvers.spec.handlebars index 5c4bd86..95abb55 100644 --- a/templates/typeTypeResolvers.spec.handlebars +++ b/templates/typeTypeResolvers.spec.handlebars @@ -1,6 +1,6 @@ import td from "testdouble"; -import { GqlContext, {{#if resolveReferenceType}}ResolversParentTypes, {{/if}} {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "@generated/graphql/types"; +import { GqlContext, ResolversParentTypes, {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "@generated/graphql/types"; import { {{typeName}}{{capitalizedFieldName}} } from "./{{typeName}}{{capitalizedFieldName}}"; {{#if resolveReferenceType}}type ParentType = Parameters[0];{{/if}} @@ -14,7 +14,6 @@ const context = td.object(); // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() - {{#unless resolveReferenceType}}args,{{/unless}} const parent{{#if resolveReferenceType}} = {} as ParentType {{else}}: ResolversParentTypes["{{typeName}}"] = {}{{/if}} {{#if hasArguments}} const variables: {{typeName}}{{capitalizedFieldName}}Args = {}