Skip to content

Commit

Permalink
feat(server fragments): server predefined fragments
Browse files Browse the repository at this point in the history
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 graphql#575
  • Loading branch information
Ariel-Dayan committed Nov 28, 2019
1 parent a7d5404 commit 9e7253f
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 5 deletions.
2 changes: 1 addition & 1 deletion resources/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const {
} = require('./utils');

if (require.main === module) {
rmdirRecursive('./dist');
// rmdirRecursive('./dist');
mkdirRecursive('./dist');

copyFile('./LICENSE', './dist/LICENSE');
Expand Down
8 changes: 5 additions & 3 deletions resources/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down
88 changes: 87 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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<string> } fragmentsInUsed - Set of relevant fragments.
*
* @returns { Set<string> } - relevant fragments for current request.
*/
function findFragments(serverFragments: string, query: string, fragmentsInUsed: Set<string>): Set<string> {
// 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}`;
}
4 changes: 4 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ declare namespace graphqlHTTP {
* `__typename` field or alternatively calls the `isTypeOf` method).
*/
typeResolver?: GraphQLTypeResolver<unknown, unknown> | null;
/**
* ssssss
*/
serverFragments: string,
}

/**
Expand Down

0 comments on commit 9e7253f

Please sign in to comment.