Skip to content
This repository was archived by the owner on Sep 3, 2021. It is now read-only.

Commit 8d84547

Browse files
authored
Merge pull request #200 from michaeldgraham/master
Passing context to @cypher, scalar payload mutations, fixes
2 parents fd2ec0b + b371eb3 commit 8d84547

9 files changed

+1107
-230
lines changed

src/augment.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ const augmentResolvers = (augmentedTypeMap, resolvers, config) => {
206206
const possiblyAddOrderingArgument = (args, fieldName) => {
207207
const orderingType = `_${fieldName}Ordering`;
208208
if (args.findIndex(e => e.name.value === orderingType) === -1) {
209-
// TODO refactor
210209
args.push({
211210
kind: 'InputValueDefinition',
212211
name: {
@@ -1370,10 +1369,10 @@ const shouldAugmentRelationField = (config, rootType, fromName, toName) =>
13701369
shouldAugmentType(config, rootType, toName);
13711370

13721371
const fieldIsNotIgnored = (astNode, field, resolvers) => {
1373-
return (
1374-
!getFieldDirective(field, 'neo4j_ignore') &&
1375-
!getCustomFieldResolver(astNode, field, resolvers)
1376-
);
1372+
return !getFieldDirective(field, 'neo4j_ignore');
1373+
// FIXME: issue related to inferences on AST field .resolve
1374+
// See: possiblyAddIgnoreDirective
1375+
// !getCustomFieldResolver(astNode, field, resolvers)
13771376
};
13781377

13791378
const isNotSystemField = name => {

src/index.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import {
55
extractTypeMapFromTypeDefs,
66
addDirectiveDeclarations,
77
printTypeMap,
8-
getQuerySelections,
9-
getMutationSelections
8+
getPayloadSelections
109
} from './utils';
1110
import {
1211
extractTypeMapFromSchema,
@@ -67,9 +66,10 @@ export function cypherQuery(
6766
) {
6867
const { typeName, variableName } = typeIdentifiers(resolveInfo.returnType);
6968
const schemaType = resolveInfo.schema.getType(typeName);
70-
const selections = getQuerySelections(resolveInfo);
69+
const selections = getPayloadSelections(resolveInfo);
7170
return translateQuery({
7271
resolveInfo,
72+
context,
7373
schemaType,
7474
selections,
7575
variableName,
@@ -89,9 +89,10 @@ export function cypherMutation(
8989
) {
9090
const { typeName, variableName } = typeIdentifiers(resolveInfo.returnType);
9191
const schemaType = resolveInfo.schema.getType(typeName);
92-
const selections = getMutationSelections(resolveInfo);
92+
const selections = getPayloadSelections(resolveInfo);
9393
return translateMutation({
9494
resolveInfo,
95+
context,
9596
schemaType,
9697
selections,
9798
variableName,

src/selections.js

+21-6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131

3232
export function buildCypherSelection({
3333
initial,
34+
cypherParams,
3435
selections,
3536
variableName,
3637
schemaType,
@@ -60,6 +61,7 @@ export function buildCypherSelection({
6061

6162
let tailParams = {
6263
selections: tailSelections,
64+
cypherParams,
6365
variableName,
6466
schemaType,
6567
resolveInfo,
@@ -104,9 +106,12 @@ export function buildCypherSelection({
104106
}
105107

106108
const commaIfTail = tailSelections.length > 0 ? ',' : '';
107-
109+
const isScalarSchemaType = isGraphqlScalarType(schemaType);
110+
const schemaTypeField = !isScalarSchemaType
111+
? schemaType.getFields()[fieldName]
112+
: {};
108113
// Schema meta fields(__schema, __typename, etc)
109-
if (!schemaType.getFields()[fieldName]) {
114+
if (!isScalarSchemaType && !schemaTypeField) {
110115
return recurse({
111116
initial: tailSelections.length
112117
? initial
@@ -115,7 +120,8 @@ export function buildCypherSelection({
115120
});
116121
}
117122

118-
const fieldType = schemaType.getFields()[fieldName].type;
123+
const fieldType =
124+
schemaTypeField && schemaTypeField.type ? schemaTypeField.type : {};
119125
const innerSchemaType = innerType(fieldType); // for target "type" aka label
120126

121127
if (
@@ -166,6 +172,7 @@ export function buildCypherSelection({
166172
return recurse({
167173
initial: `${initial}${fieldName}: apoc.cypher.runFirstColumn("${customCypher}", ${cypherDirectiveArgs(
168174
variableName,
175+
cypherParams,
169176
headSelection,
170177
schemaType,
171178
resolveInfo
@@ -192,7 +199,10 @@ export function buildCypherSelection({
192199
});
193200
}
194201
// We have a graphql object type
195-
const innerSchemaTypeAstNode = typeMap[innerSchemaType].astNode;
202+
const innerSchemaTypeAstNode =
203+
innerSchemaType && typeMap[innerSchemaType]
204+
? typeMap[innerSchemaType].astNode
205+
: {};
196206
const innerSchemaTypeRelation = getRelationTypeDirectiveArgs(
197207
innerSchemaTypeAstNode
198208
);
@@ -213,7 +223,7 @@ export function buildCypherSelection({
213223
const skipLimit = computeSkipLimit(headSelection, resolveInfo.variableValues);
214224

215225
const subSelections = extractSelections(
216-
headSelection.selectionSet.selections,
226+
headSelection.selectionSet ? headSelection.selectionSet.selections : [],
217227
resolveInfo.fragments
218228
);
219229

@@ -223,6 +233,7 @@ export function buildCypherSelection({
223233
variableName: nestedVariable,
224234
schemaType: innerSchemaType,
225235
resolveInfo,
236+
cypherParams,
226237
parentSelectionInfo: {
227238
fieldName,
228239
schemaType,
@@ -235,7 +246,10 @@ export function buildCypherSelection({
235246
});
236247

237248
let selection;
238-
const fieldArgs = schemaType.getFields()[fieldName].args.map(e => e.astNode);
249+
const fieldArgs =
250+
!isScalarSchemaType && schemaTypeField && schemaTypeField.args
251+
? schemaTypeField.args.map(e => e.astNode)
252+
: [];
239253
const temporalArgs = getTemporalArguments(fieldArgs);
240254
const queryParams = paramsToString(
241255
innerFilterParams(filterParams, temporalArgs)
@@ -259,6 +273,7 @@ export function buildCypherSelection({
259273
selection = recurse(
260274
customCypherField({
261275
...fieldInfo,
276+
cypherParams,
262277
schemaType,
263278
schemaTypeRelation,
264279
customCypher,

src/translate.js

+57-7
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ import {
3030
splitSelectionParameters,
3131
getTemporalArguments,
3232
temporalPredicateClauses,
33-
isTemporalType
33+
isTemporalType,
34+
isGraphqlScalarType
3435
} from './utils';
3536
import { getNamedType } from 'graphql';
3637
import { buildCypherSelection } from './selections';
3738
import _ from 'lodash';
3839

3940
export const customCypherField = ({
4041
customCypher,
42+
cypherParams,
4143
schemaTypeRelation,
4244
initial,
4345
fieldName,
@@ -62,6 +64,7 @@ export const customCypherField = ({
6264
fieldIsList ? '' : 'head('
6365
}[ ${nestedVariable} IN apoc.cypher.runFirstColumn("${customCypher}", ${cypherDirectiveArgs(
6466
variableName,
67+
cypherParams,
6568
headSelection,
6669
schemaType,
6770
resolveInfo
@@ -368,7 +371,7 @@ export const temporalField = ({
368371
// containing this temporal field was a node
369372
let variableName = parentVariableName;
370373
let fieldIsArray = isArrayType(parentFieldType);
371-
if (!isNodeType(parentSchemaType.astNode)) {
374+
if (parentSchemaType && !isNodeType(parentSchemaType.astNode)) {
372375
// initial assumption wrong, build appropriate relationship variable
373376
if (
374377
isRootSelection({
@@ -474,6 +477,7 @@ export const temporalType = ({
474477
// Query API root operation branch
475478
export const translateQuery = ({
476479
resolveInfo,
480+
context,
477481
selections,
478482
variableName,
479483
typeName,
@@ -493,13 +497,15 @@ export const translateQuery = ({
493497
const queryArgs = getQueryArguments(resolveInfo);
494498
const temporalArgs = getTemporalArguments(queryArgs);
495499
const queryTypeCypherDirective = getQueryCypherDirective(resolveInfo);
500+
const cypherParams = getCypherParams(context);
496501
const queryParams = paramsToString(
497502
innerFilterParams(
498503
filterParams,
499504
temporalArgs,
500505
null,
501506
queryTypeCypherDirective ? true : false
502-
)
507+
),
508+
cypherParams
503509
);
504510
const safeVariableName = safeVar(variableName);
505511
const temporalClauses = temporalPredicateClauses(
@@ -509,9 +515,11 @@ export const translateQuery = ({
509515
);
510516
const outerSkipLimit = getOuterSkipLimit(first);
511517
const orderByValue = computeOrderBy(resolveInfo, selections);
518+
512519
if (queryTypeCypherDirective) {
513520
return customQuery({
514521
resolveInfo,
522+
cypherParams,
515523
schemaType,
516524
argString: queryParams,
517525
selections,
@@ -525,6 +533,7 @@ export const translateQuery = ({
525533
} else {
526534
return nodeQuery({
527535
resolveInfo,
536+
cypherParams,
528537
schemaType,
529538
argString: queryParams,
530539
selections,
@@ -542,9 +551,19 @@ export const translateQuery = ({
542551
}
543552
};
544553

554+
const getCypherParams = context => {
555+
return context &&
556+
context.cypherParams &&
557+
context.cypherParams instanceof Object &&
558+
Object.keys(context.cypherParams).length > 0
559+
? context.cypherParams
560+
: undefined;
561+
};
562+
545563
// Custom read operation
546564
const customQuery = ({
547565
resolveInfo,
566+
cypherParams,
548567
schemaType,
549568
argString,
550569
selections,
@@ -558,27 +577,40 @@ const customQuery = ({
558577
const safeVariableName = safeVar(variableName);
559578
const [subQuery, subParams] = buildCypherSelection({
560579
initial: '',
580+
cypherParams,
561581
selections,
562582
variableName,
563583
schemaType,
564584
resolveInfo,
565585
paramIndex: 1
566586
});
567587
const params = { ...nonNullParams, ...subParams };
588+
if (cypherParams) {
589+
params['cypherParams'] = cypherParams;
590+
}
568591
// QueryType with a @cypher directive
569592
const cypherQueryArg = queryTypeCypherDirective.arguments.find(x => {
570593
return x.name.value === 'statement';
571594
});
595+
const isScalarType = isGraphqlScalarType(schemaType);
596+
const temporalType = isTemporalType(schemaType.name);
572597
const query = `WITH apoc.cypher.runFirstColumn("${
573598
cypherQueryArg.value.value
574-
}", ${argString || 'null'}, True) AS x UNWIND x AS ${safeVariableName}
575-
RETURN ${safeVariableName} {${subQuery}} AS ${safeVariableName}${orderByValue} ${outerSkipLimit}`;
599+
}", ${argString ||
600+
'null'}, True) AS x UNWIND x AS ${safeVariableName} RETURN ${safeVariableName} ${
601+
// Don't add subQuery for scalar type payloads
602+
// FIXME: fix subselection translation for temporal type payload
603+
!temporalType && !isScalarType
604+
? `{${subQuery}} AS ${safeVariableName}${orderByValue}`
605+
: ''
606+
} ${outerSkipLimit}`;
576607
return [query, params];
577608
};
578609

579610
// Generated API
580611
const nodeQuery = ({
581612
resolveInfo,
613+
cypherParams,
582614
schemaType,
583615
selections,
584616
variableName,
@@ -596,13 +628,17 @@ const nodeQuery = ({
596628
const safeLabelName = safeLabel(typeName);
597629
const [subQuery, subParams] = buildCypherSelection({
598630
initial: '',
631+
cypherParams,
599632
selections,
600633
variableName,
601634
schemaType,
602635
resolveInfo,
603636
paramIndex: 1
604637
});
605638
const params = { ...nonNullParams, ...subParams };
639+
if (cypherParams) {
640+
params['cypherParams'] = cypherParams;
641+
}
606642
const arrayParams = _.pickBy(filterParams, Array.isArray);
607643
const args = innerFilterParams(filterParams, temporalArgs);
608644

@@ -642,6 +678,7 @@ const nodeQuery = ({
642678
// Mutation API root operation branch
643679
export const translateMutation = ({
644680
resolveInfo,
681+
context,
645682
schemaType,
646683
selections,
647684
variableName,
@@ -669,6 +706,7 @@ export const translateMutation = ({
669706
if (mutationTypeCypherDirective) {
670707
return customMutation({
671708
...mutationInfo,
709+
context,
672710
mutationTypeCypherDirective,
673711
variableName,
674712
orderByValue,
@@ -712,6 +750,7 @@ export const translateMutation = ({
712750
// Custom write operation
713751
const customMutation = ({
714752
params,
753+
context,
715754
mutationTypeCypherDirective,
716755
selections,
717756
variableName,
@@ -720,6 +759,7 @@ const customMutation = ({
720759
orderByValue,
721760
outerSkipLimit
722761
}) => {
762+
const cypherParams = getCypherParams(context);
723763
const safeVariableName = safeVar(variableName);
724764
// FIXME: support IN for multiple values -> WHERE
725765
const argString = paramsToString(
@@ -728,7 +768,8 @@ const customMutation = ({
728768
null,
729769
null,
730770
true
731-
)
771+
),
772+
cypherParams
732773
);
733774
const cypherQueryArg = mutationTypeCypherDirective.arguments.find(x => {
734775
return x.name.value === 'statement';
@@ -741,12 +782,21 @@ const customMutation = ({
741782
resolveInfo,
742783
paramIndex: 1
743784
});
785+
const isScalarType = isGraphqlScalarType(schemaType);
786+
const temporalType = isTemporalType(schemaType.name);
744787
params = { ...params, ...subParams };
788+
if (cypherParams) {
789+
params['cypherParams'] = cypherParams;
790+
}
745791
const query = `CALL apoc.cypher.doIt("${
746792
cypherQueryArg.value.value
747793
}", ${argString}) YIELD value
748794
WITH apoc.map.values(value, [keys(value)[0]])[0] AS ${safeVariableName}
749-
RETURN ${safeVariableName} {${subQuery}} AS ${safeVariableName}${orderByValue} ${outerSkipLimit}`;
795+
RETURN ${safeVariableName} ${
796+
!temporalType && !isScalarType
797+
? `{${subQuery}} AS ${safeVariableName}${orderByValue} ${outerSkipLimit}`
798+
: ''
799+
}`;
750800
return [query, params];
751801
};
752802

0 commit comments

Comments
 (0)