Skip to content

Commit

Permalink
feat: Add support for defragmenting a query (i.e. injecting all fragm…
Browse files Browse the repository at this point in the history
…ents into the operation)
  • Loading branch information
nicolasdao committed Jan 11, 2018
1 parent 71973c9 commit 058c49c
Show file tree
Hide file tree
Showing 6 changed files with 401 additions and 63 deletions.
150 changes: 126 additions & 24 deletions lib/graphqls2s.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ var _require2 = __webpack_require__(33),
* GraphQL document and/or execution result that correspond to the Error.
*/

var GraphQLError = function GraphQLError( // eslint-disable-line no-redeclare
function GraphQLError( // eslint-disable-line no-redeclare
message, nodes, source, positions, path, originalError, extensions) {
// Compute list of blame nodes.
var _nodes = Array.isArray(nodes) ? nodes.length !== 0 ? nodes : undefined : nodes ? [nodes] : undefined;
Expand Down Expand Up @@ -405,7 +405,7 @@ message, nodes, source, positions, path, originalError, extensions) {
}, []);
}

(0, _defineProperties2.default)(undefined, {
(0, _defineProperties2.default)(this, {
message: {
value: message,
// By being enumerable, JSON.stringify will include `message` in the
Expand Down Expand Up @@ -451,21 +451,21 @@ message, nodes, source, positions, path, originalError, extensions) {

// Include (non-enumerable) stack trace.
if (originalError && originalError.stack) {
Object.defineProperty(undefined, 'stack', {
Object.defineProperty(this, 'stack', {
value: originalError.stack,
writable: true,
configurable: true
});
} else if (Error.captureStackTrace) {
Error.captureStackTrace(undefined, GraphQLError);
Error.captureStackTrace(this, GraphQLError);
} else {
Object.defineProperty(undefined, 'stack', {
Object.defineProperty(this, 'stack', {
value: Error().stack,
writable: true,
configurable: true
});
}
};
}

GraphQLError.prototype = (0, _create2.default)(Error.prototype, {
constructor: { value: GraphQLError },
Expand Down Expand Up @@ -964,8 +964,9 @@ var getQueryFields = function getQueryFields(queryProp, parentTypeAST, schemaAST
return chain(parentTypeAST.blockProps.find(function (x) {
return x.details.name == removeAlias(queryProp.name);
})).next(function (schemaProp) {
return !schemaProp ? throwError(true, 'Error in method \'getQueryFields\': Query function \'' + queryProp.name + '\' is not defined in the GraphQL schema (specifically in the \'parentTypeAST\' argument).') : {
if (schemaProp) return {
name: queryProp.name,
kind: queryProp.kind,
type: schemaProp.details.result.name,
metadata: schemaProp.details.metadata,
isNode: isNodeType(schemaProp.details.result.name, schemaAST),
Expand All @@ -981,8 +982,17 @@ var getQueryFields = function getQueryFields(queryProp, parentTypeAST, schemaAST
return parentTypeAST ? queryProp.properties.map(function (queryProp) {
return getQueryFields(queryProp, parentTypeAST, schemaAST);
}) : throwError(true, 'Error in method \'getQueryFields\': Cannot find type \'' + schemaProp.details.result.name + '\' in the GraphQL Schema.');
}).val() : null,
utils: { isEnum: schemaAST.isEnum }
}).val() : null
};else return {
name: queryProp.name,
kind: queryProp.kind,
type: null,
metadata: null,
isNode: null,
edge: null,
args: queryProp.args,
properties: queryProp.properties,
error: schemaProp ? null : 'Error in method \'getQueryFields\': Query function \'' + queryProp.name + '\' is not defined in the GraphQL schema (specifically in the \'parentTypeAST\' argument).'
};
}).val();
};
Expand Down Expand Up @@ -1021,7 +1031,8 @@ var parseProperties = function parseProperties(selectionSet) {
return {
name: '' + (x.alias ? x.alias.value + ':' : '') + x.name.value,
args: parseArguments(x.arguments),
properties: parseProperties(x.selectionSet)
properties: parseProperties(x.selectionSet),
kind: x.kind
};
});
};
Expand Down Expand Up @@ -1049,9 +1060,35 @@ var parseArguments = function parseArguments(astArgs) {
});
};

var _graphQlQueryTypes = { 'query': 'Query', 'mutation': 'Mutation', 'subscription': 'Subscription' };
var getQueryAST = function getQueryAST(query, schemaAST) {
var ast = ((parse(query) || {}).definitions || [])[0];
var parseFragments = function parseFragments() {
var fragments = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
return fragments.length == 0 ? null : fragments.map(function (fragment) {
return {
name: (fragment.name || {}).value,
type: ((fragment.typeCondition || {}).name || {}).value,
properties: parseProperties(fragment.selectionSet)
};
});
};

var _graphQlQueryTypes = { 'query': 'Query', 'mutation': 'Mutation', 'subscription': 'Subscription'
/**
* [description]
* @param {[type]} query [description]
* @param {[type]} schemaAST [description]
* @param {Boolean} options.defrag [description]
* @return {[type]} [description]
*/
};var getQueryAST = function getQueryAST(query, schemaAST) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

var parsedQuery = (parse(query) || {}).definitions || [];
var ast = parsedQuery.find(function (x) {
return x.kind == 'OperationDefinition';
});
var fragments = parsedQuery.filter(function (x) {
return x.kind == 'FragmentDefinition';
});
if (ast) {
var operation = {
type: ast.operation,
Expand All @@ -1061,17 +1098,14 @@ var getQueryAST = function getQueryAST(query, schemaAST) {
t = _ref3.type;
return { name: v.name.value, type: t.name.value };
}) : null,
body: parseProperties(ast.selectionSet)
body: parseProperties(ast.selectionSet),
fragments: parseFragments(fragments)
};
if (operation.body && operation.body.some(function (x) {
return x.name == '__schema' || x.name == '__type';
})) return { error: 'schema_data_not_supported', message: 'This version of graphql-s2s does not support analysing graphql queries about the schema itself.' };else {
var output = getQueryOrMutationAST(operation, schemaAST, _graphQlQueryTypes[ast.operation]);
(0, _assign2.default)(output, { filter: function filter(fn) {
return filterQueryAST(output, fn);
} });
return output;
}
var output = getQueryOrMutationAST(operation, schemaAST, _graphQlQueryTypes[ast.operation]);
(0, _assign2.default)(output, { filter: function filter(fn) {
return filterQueryAST(output, fn);
} });
return options.defrag ? defrag(output) : output;
} else return null;
};

Expand Down Expand Up @@ -1116,15 +1150,24 @@ var buildQuery = function buildQuery() {
return buildSingleQuery(a);
}).join('\n')).next(function (body) {
return (skipOperationParsing ? '' : stringifyOperation(operation)) + '{\n' + body + '\n}';
}).next(function (op) {
return operation.fragments && operation.fragments.length > 0 ? op + '\n' + stringifyFragments(operation.fragments) : op;
}).val();
};

var stringifyFragments = function stringifyFragments() {
var fragments = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
return fragments.map(function (f) {
return 'fragment ' + f.name + ' on ' + f.type + ' ' + buildQuery({ body: f.properties }, true);
}).join('\n');
};

var buildSingleQuery = function buildSingleQuery(AST) {
if (AST && AST.name) {
var fnName = AST.name;
var args = AST.args ? stringifyArgs(AST.args).trim() : '';
var fields = AST.properties && AST.properties.length > 0 ? buildQuery({ body: AST.properties }, true) : '';
return '' + fnName + (args ? '(' + args + ')' : '') + fields;
return AST.kind == 'FragmentSpread' ? '...' + fnName : '' + fnName + (args ? '(' + args + ')' : '') + fields;
} else return '';
};

Expand All @@ -1142,6 +1185,65 @@ var stringifyArgs = function stringifyArgs() {
}).join(',');
};

var _defragCache = {};
var defrag = function defrag(operation) {
if (operation && operation.fragments && operation.fragments.length > 0) {
// reset cache
_defragCache = {};
var body = replaceFragmentsInProperties(operation.body, operation.fragments);
// reset cache
_defragCache = {};
return (0, _assign2.default)({}, operation, { body: body, fragments: null });
} else return operation;
};

var replaceFragmentsInProperty = function replaceFragmentsInProperty(prop) {
var fragments = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

if (prop.kind == 'FragmentSpread') {
var fragmentName = prop.name;
var fragment = fragments.find(function (f) {
return f.name == fragmentName;
});
if (!fragment) throw new Error('Invalid GraphQL query. Fragment \'' + fragmentName + '\' does not exist.');

if (!_defragCache[fragmentName]) _defragCache[fragmentName] = replaceFragmentsInProperties(fragment.properties, fragments);

return _defragCache[fragmentName];
} else if (prop.properties && prop.properties.length > 0) {
var properties = replaceFragmentsInProperties(prop.properties, fragments);
return (0, _assign2.default)({}, prop, { properties: properties });
} else return prop;
};

var replaceFragmentsInProperties = function replaceFragmentsInProperties(properties) {
var fragments = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

if (properties && properties.length > 0) {
var propertiesObj = properties.reduce(function (props, p) {
var _p = replaceFragmentsInProperty(p, fragments);
if (Array.isArray(_p)) {
_p.forEach(function (property) {
var existingProp = props[property.name];
// Save it if this property is new or if the existing property does not have a metadata property
// WARNING: metadata == undefined is better than metadata == null as it really proves that metadata
// has never been set.
if (!existingProp || existingProp.metadata == undefined) props[property.name] = property;
});
} else {
var existingProp = props[_p.name];
if (!existingProp || existingProp.metadata == undefined) props[_p.name] = _p;
}
return props;
}, {});

var results = [];
for (var key in propertiesObj) {
results.push(propertiesObj[key]);
}return results;
} else return null;
};

module.exports = {
chain: chain,
log: log,
Expand Down
2 changes: 1 addition & 1 deletion lib/graphqls2s.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/graphqls2s.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/graphql/error/GraphQLError.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const { getLocation } = require('../location')
*/


let GraphQLError = ( // eslint-disable-line no-redeclare
message, nodes, source, positions, path, originalError, extensions) => {
function GraphQLError( // eslint-disable-line no-redeclare
message, nodes, source, positions, path, originalError, extensions) {
// Compute list of blame nodes.
var _nodes = Array.isArray(nodes) ? nodes.length !== 0 ? nodes : undefined : nodes ? [nodes] : undefined

Expand Down
Loading

0 comments on commit 058c49c

Please sign in to comment.