From 9e7253f01dbab5cc9048c4b66468735e3767d514 Mon Sep 17 00:00:00 2001 From: Ariel Date: Thu, 28 Nov 2019 16:12:32 +0200 Subject: [PATCH 1/2] feat(server fragments): server predefined fragments added function addedServerFragments that concats relevent fragments from the server to req query. added function findFragments that return relevant fragments for current request. added function sliceFirstWord. added serverFragments option. resolves #575 --- resources/build.js | 2 +- resources/utils.js | 8 +++-- src/index.js | 88 +++++++++++++++++++++++++++++++++++++++++++++- types/index.d.ts | 4 +++ 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/resources/build.js b/resources/build.js index 8551f1ef..979b1399 100644 --- a/resources/build.js +++ b/resources/build.js @@ -16,7 +16,7 @@ const { } = require('./utils'); if (require.main === module) { - rmdirRecursive('./dist'); + // rmdirRecursive('./dist'); mkdirRecursive('./dist'); copyFile('./LICENSE', './dist/LICENSE'); diff --git a/resources/utils.js b/resources/utils.js index 75df766d..13b15e4d 100644 --- a/resources/utils.js +++ b/resources/utils.js @@ -38,12 +38,14 @@ function removeTrailingNewLine(str) { } function mkdirRecursive(dirPath) { + if(!fs.existsSync(dirPath)) fs.mkdirSync(dirPath, { recursive: true }); } function rmdirRecursive(dirPath) { if (fs.existsSync(dirPath)) { for (const dirent of fs.readdirSync(dirPath, { withFileTypes: true })) { + console.log("-------" + dirPath) const fullPath = path.join(dirPath, dirent.name); if (dirent.isDirectory()) { rmdirRecursive(fullPath); @@ -59,9 +61,9 @@ function readdirRecursive(dirPath, opts = {}) { const { ignoreDir } = opts; const result = []; for (const dirent of fs.readdirSync(dirPath, { withFileTypes: true })) { - const name = dirent.name; - if (!dirent.isDirectory()) { - result.push(dirent.name); + const name = dirent; + if (!fs.statSync(path.resolve(dirPath, name)).isDirectory()) { + result.push(name); continue; } diff --git a/src/index.js b/src/index.js index 72f98371..33b423ae 100644 --- a/src/index.js +++ b/src/index.js @@ -238,6 +238,8 @@ function graphqlHTTP(options: Options): Middleware { const typeResolver = optionsData.typeResolver; const validationRules = optionsData.validationRules || []; const graphiql = optionsData.graphiql; + const serverFragments = optionsData.serverFragments; + context = optionsData.context || request; // GraphQL HTTP only supports GET and POST methods. @@ -247,7 +249,7 @@ function graphqlHTTP(options: Options): Middleware { } // Get GraphQL params from the request and POST body data. - query = params.query; + query = serverFragments ? addServerFragments(serverFragments, params.query) : params.query; variables = params.variables; operationName = params.operationName; showGraphiQL = canDisplayGraphiQL(request, params) && graphiql; @@ -509,3 +511,87 @@ function sendResponse(response: $Response, type: string, data: string): void { response.setHeader('Content-Length', String(chunk.length)); response.end(chunk); } + +/** + * Helper function to get the first word from string. + * + * @param { string } text - The full string. + * + * @returns { string } - First word. + */ +function sliceFirstWord(text: string): string { + let slicedText = text; + + const firstSpaceIndex = slicedText.indexOf(' '); + + if(firstSpaceIndex !== -1) { + slicedText = slicedText.slice(0, firstSpaceIndex); + } + + const firstEndRowIndex = slicedText.indexOf('\n'); + + if(firstEndRowIndex !== -1) { + slicedText = slicedText.slice(0, firstEndRowIndex); + } + + return slicedText; +} + +/** + * Helper recursive function that finds all the fragments from the server that are used in the current request. + * + * @param { string } serverFragments - Fragments from the server. + * @param { string } query - Query from the request. + * @param { Set } fragmentsInUsed - Set of relevant fragments. + * + * @returns { Set } - relevant fragments for current request. + */ +function findFragments(serverFragments: string, query: string, fragmentsInUsed: Set): Set { + // Fragment declaration starts with 'fragment' key word + // Slice to remove text before the first fragment declaration + let fragmentDeclarationFields = serverFragments.split('fragment ').slice(1); + + // Fragment variable starts with spread - '...' + // Slice to remove text before the first fragment variable + let fragmentVariableFields = query.split('...').slice(1); + + fragmentVariableFields.forEach(fragmentVariable => { + const currFragmentVariableKeyName = sliceFirstWord(fragmentVariable); + + for (let index = 0; index < fragmentDeclarationFields.length; index++) { + const currFragmentDeclaration = fragmentDeclarationFields[index]; + const currFragmentDeclarationKeyName = sliceFirstWord(currFragmentDeclaration); + + if(currFragmentDeclarationKeyName === currFragmentVariableKeyName) { + + fragmentsInUsed.add(currFragmentDeclaration); + + // Find fragments in the matching fragments + fragmentsInUsed = findFragments(serverFragments, currFragmentDeclaration, fragmentsInUsed) + + break; + } + + } + }) + + return fragmentsInUsed; +} + +/** + * Add to query the relevant server fragments + * + * @param {*} serverFragments - Fragments from the server. + * @param {*} query - Query from the request. + * + * @returns - Concat relevant fragments to request query + */ +function addServerFragments(serverFragments: string, query: string): string { + let fragmentsInUsed = ''; + + [...(findFragments(serverFragments, query, new Set()))].forEach(fullFragment => { + fragmentsInUsed += `fragment ${fullFragment} `; + }) + + return `${fragmentsInUsed}\n${query}`; +} \ No newline at end of file diff --git a/types/index.d.ts b/types/index.d.ts index 1a7e9495..159fab1b 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -137,6 +137,10 @@ declare namespace graphqlHTTP { * `__typename` field or alternatively calls the `isTypeOf` method). */ typeResolver?: GraphQLTypeResolver | null; + /** + * ssssss + */ + serverFragments: string, } /** From 6f48c0eb35428a9a3dbb81f4b86ba6fa019805fd Mon Sep 17 00:00:00 2001 From: Ariel Date: Thu, 28 Nov 2019 17:42:05 +0200 Subject: [PATCH 2/2] fix(build and declaration): build and declare interface and type returned build to previous version. added serverFragments to OptionsData in index.js. added serverFragments to OptionsData in types/index.d.ts --- package.json | 4 ++-- resources/build.js | 2 +- resources/utils.js | 8 +++----- src/index.js | 5 +++++ types/index.d.ts | 5 +++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a9f5cb6e..c1df4bc9 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "private": true, "main": "index.js", "types": "index.d.ts", - "homepage": "https://github.com/graphql/express-graphql", + "homepage": "https://github.com/Ariel-Dayan/express-graphql.git", "bugs": { "url": "https://github.com/graphql/express-graphql/issues" }, "repository": { "type": "git", - "url": "https://github.com/graphql/express-graphql.git" + "url": "https://github.com/Ariel-Dayan/express-graphql.git" }, "keywords": [ "express", diff --git a/resources/build.js b/resources/build.js index 979b1399..8551f1ef 100644 --- a/resources/build.js +++ b/resources/build.js @@ -16,7 +16,7 @@ const { } = require('./utils'); if (require.main === module) { - // rmdirRecursive('./dist'); + rmdirRecursive('./dist'); mkdirRecursive('./dist'); copyFile('./LICENSE', './dist/LICENSE'); diff --git a/resources/utils.js b/resources/utils.js index 13b15e4d..75df766d 100644 --- a/resources/utils.js +++ b/resources/utils.js @@ -38,14 +38,12 @@ function removeTrailingNewLine(str) { } function mkdirRecursive(dirPath) { - if(!fs.existsSync(dirPath)) fs.mkdirSync(dirPath, { recursive: true }); } function rmdirRecursive(dirPath) { if (fs.existsSync(dirPath)) { for (const dirent of fs.readdirSync(dirPath, { withFileTypes: true })) { - console.log("-------" + dirPath) const fullPath = path.join(dirPath, dirent.name); if (dirent.isDirectory()) { rmdirRecursive(fullPath); @@ -61,9 +59,9 @@ function readdirRecursive(dirPath, opts = {}) { const { ignoreDir } = opts; const result = []; for (const dirent of fs.readdirSync(dirPath, { withFileTypes: true })) { - const name = dirent; - if (!fs.statSync(path.resolve(dirPath, name)).isDirectory()) { - result.push(name); + const name = dirent.name; + if (!dirent.isDirectory()) { + result.push(dirent.name); continue; } diff --git a/src/index.js b/src/index.js index 33b423ae..98296965 100644 --- a/src/index.js +++ b/src/index.js @@ -140,6 +140,11 @@ export type OptionsData = {| * `__typename` field or alternatively calls the `isTypeOf` method). */ typeResolver?: ?GraphQLTypeResolver, + + /** + * A optional string which will used to predefined fragments + */ + serverFragments?: ?string, |}; /** diff --git a/types/index.d.ts b/types/index.d.ts index 159fab1b..82532c05 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -137,10 +137,11 @@ declare namespace graphqlHTTP { * `__typename` field or alternatively calls the `isTypeOf` method). */ typeResolver?: GraphQLTypeResolver | null; + /** - * ssssss + * A optional string which will used to predefined fragments */ - serverFragments: string, + serverFragments?: string; } /**