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

Commit 7246b50

Browse files
Input object argument format for mutation API (#531)
* makes test script more specific * exports some filter argument builders * experimental support for where / data mutation arguments * Update node.js * experimental support for where / data mutation arguments * experimental support for where / data mutation arguments * experimental support for where / data mutation arguments * exports schema comparison helpers now used by both the normal and experimental augmentation tests * adds test for experimental augmented schema * adds typeDefs for experimental augmentation test * Update augmentSchemaTest.test.js * augmentation test for experimental schema * adds tests for experimental node and relationship mutation arguments * removes unused arguments and branches on array emptiness * fixed temporal ordering and filtering #524: unified the translation of nested orderBy arguments for relationship fields into translateNestedOrderingArgument, fixed schemaType argument to be innerSchemaType, for call in relationFieldOnNodeType #495: uses parentIsListArgument to buildNeo4jTypeTranslation, to appropriately translate temporal filters used within OR / AND list filters * adds tests for fixing #495 and #524 * blocks empty string "" from passing this results in letting the cypher error pass through, caused by datetime(""), if an empty string is provided for a .formatted argument * removed now unused function argument * Update input-values.js
1 parent c4ed242 commit 7246b50

16 files changed

+2793
-555
lines changed

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"build-with-sourcemaps": "babel src --presets @babel/preset-env --out-dir dist --source-maps",
1717
"precommit": "lint-staged",
1818
"prepare": "npm run build",
19-
"test": "nyc --reporter=lcov ava test/unit/**.test.js --verbose",
19+
"test": "nyc --reporter=lcov ava test/unit/augmentSchemaTest.test.js test/unit/configTest.test.js test/unit/assertSchema.test.js test/unit/cypherTest.test.js test/unit/filterTest.test.js test/unit/filterTests.test.js test/unit/experimental/augmentSchemaTest.test.js test/unit/experimental/cypherTest.test.js",
2020
"parse-tck": "babel-node test/helpers/tck/parseTck.js",
2121
"test-tck": "nyc ava --fail-fast test/unit/filterTests.test.js",
2222
"report-coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",

Diff for: src/augment/input-values.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ const LogicalFilteringArgument = {
333333
/**
334334
* Builds the AST definitions for logical filtering arguments
335335
*/
336-
const buildLogicalFilterInputValues = ({ typeName = '' }) => {
336+
export const buildLogicalFilterInputValues = ({ typeName = '' }) => {
337337
return [
338338
buildInputValue({
339339
name: buildName({ name: LogicalFilteringArgument.AND }),
@@ -361,7 +361,7 @@ const buildLogicalFilterInputValues = ({ typeName = '' }) => {
361361
/**
362362
* Builds the AST definitions for filtering Neo4j property type fields
363363
*/
364-
const buildPropertyFilters = ({
364+
export const buildPropertyFilters = ({
365365
field,
366366
fieldName = '',
367367
outputType = '',
@@ -512,11 +512,10 @@ export const selectUnselectedOrderedFields = ({
512512
);
513513
const orderingArgumentFieldNames = Object.keys(orderedFieldNameMap);
514514
orderingArgumentFieldNames.forEach(orderedFieldName => {
515-
if (
516-
!fieldSelectionSet.some(
517-
field => field.name && field.name.value === orderedFieldName
518-
)
519-
) {
515+
const orderedFieldAlreadySelected = fieldSelectionSet.some(
516+
field => field.name && field.name.value === orderedFieldName
517+
);
518+
if (!orderedFieldAlreadySelected) {
520519
// add the field so that its data can be used for ordering
521520
// since as it is not actually selected, it will be removed
522521
// by default GraphQL post-processing field resolvers

Diff for: src/augment/types/node/mutation.js

+166-83
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import {
1212
useAuthDirective,
1313
isCypherField
1414
} from '../../directives';
15-
import { getPrimaryKey } from './selection';
15+
import {
16+
getPrimaryKey,
17+
buildNodeSelectionInputType,
18+
buildNodeSelectionInputTypes
19+
} from './selection';
1620
import { shouldAugmentType } from '../../augment';
1721
import { OperationType } from '../../types/types';
1822
import {
@@ -48,6 +52,7 @@ export const augmentNodeMutationAPI = ({
4852
propertyInputValues,
4953
generatedTypeMap,
5054
operationTypeMap,
55+
typeDefinitionMap,
5156
typeExtensionDefinitionMap,
5257
config
5358
}) => {
@@ -78,89 +83,28 @@ export const augmentNodeMutationAPI = ({
7883
});
7984
});
8085
}
81-
return [operationTypeMap, generatedTypeMap];
82-
};
83-
84-
/**
85-
* Given the results of augmentNodeTypeFields, builds the AST
86-
* definition for a Mutation operation field of a given
87-
* NodeMutation name
88-
*/
89-
const buildNodeMutationField = ({
90-
mutationType,
91-
mutationAction = '',
92-
primaryKey,
93-
typeName,
94-
propertyInputValues,
95-
operationTypeMap,
96-
typeExtensionDefinitionMap,
97-
config
98-
}) => {
99-
const mutationFields = mutationType.fields;
100-
const mutationName = `${mutationAction}${typeName}`;
101-
const mutationTypeName = mutationType ? mutationType.name.value : '';
102-
const mutationTypeExtensions = typeExtensionDefinitionMap[mutationTypeName];
103-
if (
104-
!getFieldDefinition({
105-
fields: mutationFields,
106-
name: mutationName
107-
}) &&
108-
!getTypeExtensionFieldDefinition({
109-
typeExtensions: mutationTypeExtensions,
110-
name: typeName
111-
})
112-
) {
113-
const mutationConfig = {
114-
name: buildName({ name: mutationName }),
115-
args: buildNodeMutationArguments({
116-
operationName: mutationAction,
117-
primaryKey,
118-
args: propertyInputValues
119-
}),
120-
type: buildNamedType({
121-
name: typeName
122-
}),
123-
directives: buildNodeMutationDirectives({
124-
mutationAction,
125-
typeName,
126-
config
127-
})
128-
};
129-
let mutationField = undefined;
130-
let mutationDescriptionUrl = '';
131-
if (mutationAction === NodeMutation.CREATE) {
132-
mutationField = mutationConfig;
133-
mutationDescriptionUrl =
134-
'[creating](https://neo4j.com/docs/cypher-manual/4.1/clauses/create/#create-nodes)';
135-
} else if (mutationAction === NodeMutation.UPDATE) {
136-
if (primaryKey && mutationConfig.args.length > 1) {
137-
mutationField = mutationConfig;
138-
mutationDescriptionUrl =
139-
'[updating](https://neo4j.com/docs/cypher-manual/4.1/clauses/set/#set-update-a-property)';
140-
}
141-
} else if (mutationAction === NodeMutation.MERGE) {
142-
if (primaryKey) {
143-
mutationField = mutationConfig;
144-
mutationDescriptionUrl =
145-
'[merging](https://neo4j.com/docs/cypher-manual/4.1/clauses/merge/#query-merge-node-derived)';
146-
}
147-
} else if (mutationAction === NodeMutation.DELETE) {
148-
if (primaryKey) {
149-
mutationField = mutationConfig;
150-
mutationDescriptionUrl =
151-
'[deleting](https://neo4j.com/docs/cypher-manual/4.1/clauses/delete/#delete-delete-single-node)';
152-
}
153-
}
154-
if (mutationField) {
155-
mutationField.description = buildDescription({
156-
value: `[Generated mutation](${GRANDSTACK_DOCS_SCHEMA_AUGMENTATION}/#${mutationAction.toLowerCase()}) for ${mutationDescriptionUrl} a ${typeName} node.`,
157-
config
158-
});
159-
mutationFields.push(buildField(mutationField));
160-
}
161-
operationTypeMap[OperationType.MUTATION].fields = mutationFields;
86+
if (config.experimental === true) {
87+
generatedTypeMap = buildNodeSelectionInputTypes({
88+
definition,
89+
typeName,
90+
propertyInputValues,
91+
generatedTypeMap,
92+
typeDefinitionMap,
93+
typeExtensionDefinitionMap,
94+
config
95+
});
96+
} else {
97+
generatedTypeMap = buildNodeSelectionInputType({
98+
definition,
99+
typeName,
100+
propertyInputValues,
101+
generatedTypeMap,
102+
typeDefinitionMap,
103+
typeExtensionDefinitionMap,
104+
config
105+
});
162106
}
163-
return operationTypeMap;
107+
return [operationTypeMap, generatedTypeMap];
164108
};
165109

166110
/**
@@ -258,6 +202,145 @@ const buildNodeMutationArguments = ({
258202
);
259203
};
260204

205+
const buildNodeMutationObjectArguments = ({ typeName, operationName = '' }) => {
206+
const args = [];
207+
const nodeSelectionConfig = {
208+
name: 'where',
209+
type: {
210+
name: `_${typeName}Where`,
211+
wrappers: {
212+
[TypeWrappers.NON_NULL_NAMED_TYPE]: true
213+
}
214+
}
215+
};
216+
const propertyInputConfig = {
217+
name: 'data',
218+
type: {
219+
name: `_${typeName}Data`,
220+
wrappers: {
221+
[TypeWrappers.NON_NULL_NAMED_TYPE]: true
222+
}
223+
}
224+
};
225+
if (operationName === NodeMutation.CREATE) {
226+
args.push(propertyInputConfig);
227+
} else if (operationName === NodeMutation.UPDATE) {
228+
args.push(nodeSelectionConfig);
229+
args.push(propertyInputConfig);
230+
} else if (operationName === NodeMutation.MERGE) {
231+
const keySelectionInputConfig = {
232+
name: 'where',
233+
type: {
234+
name: `_${typeName}Keys`,
235+
wrappers: {
236+
[TypeWrappers.NON_NULL_NAMED_TYPE]: true
237+
}
238+
}
239+
};
240+
args.push(keySelectionInputConfig);
241+
args.push(propertyInputConfig);
242+
} else if (operationName === NodeMutation.DELETE) {
243+
args.push(nodeSelectionConfig);
244+
}
245+
return args.map(arg =>
246+
buildInputValue({
247+
name: buildName({ name: arg.name }),
248+
type: buildNamedType(arg.type)
249+
})
250+
);
251+
};
252+
253+
/**
254+
* Given the results of augmentNodeTypeFields, builds the AST
255+
* definition for a Mutation operation field of a given
256+
* NodeMutation name
257+
*/
258+
const buildNodeMutationField = ({
259+
mutationType,
260+
mutationAction = '',
261+
primaryKey,
262+
typeName,
263+
propertyInputValues,
264+
operationTypeMap,
265+
typeExtensionDefinitionMap,
266+
config
267+
}) => {
268+
const mutationFields = mutationType.fields;
269+
const mutationName = `${mutationAction}${typeName}`;
270+
const mutationTypeName = mutationType ? mutationType.name.value : '';
271+
const mutationTypeExtensions = typeExtensionDefinitionMap[mutationTypeName];
272+
if (
273+
!getFieldDefinition({
274+
fields: mutationFields,
275+
name: mutationName
276+
}) &&
277+
!getTypeExtensionFieldDefinition({
278+
typeExtensions: mutationTypeExtensions,
279+
name: typeName
280+
})
281+
) {
282+
let mutationArgs = [];
283+
if (config.experimental === true) {
284+
mutationArgs = buildNodeMutationObjectArguments({
285+
typeName,
286+
operationName: mutationAction
287+
});
288+
} else {
289+
mutationArgs = buildNodeMutationArguments({
290+
operationName: mutationAction,
291+
primaryKey,
292+
args: propertyInputValues
293+
});
294+
}
295+
const mutationConfig = {
296+
name: buildName({ name: mutationName }),
297+
args: mutationArgs,
298+
type: buildNamedType({
299+
name: typeName
300+
}),
301+
directives: buildNodeMutationDirectives({
302+
mutationAction,
303+
typeName,
304+
config
305+
})
306+
};
307+
let mutationField = undefined;
308+
let mutationDescriptionUrl = '';
309+
if (mutationAction === NodeMutation.CREATE) {
310+
mutationField = mutationConfig;
311+
mutationDescriptionUrl =
312+
'[creating](https://neo4j.com/docs/cypher-manual/4.1/clauses/create/#create-nodes)';
313+
} else if (mutationAction === NodeMutation.UPDATE) {
314+
if (primaryKey && mutationConfig.args.length > 1) {
315+
mutationField = mutationConfig;
316+
mutationDescriptionUrl =
317+
'[updating](https://neo4j.com/docs/cypher-manual/4.1/clauses/set/#set-update-a-property)';
318+
}
319+
} else if (mutationAction === NodeMutation.MERGE) {
320+
if (primaryKey) {
321+
mutationField = mutationConfig;
322+
mutationDescriptionUrl =
323+
'[merging](https://neo4j.com/docs/cypher-manual/4.1/clauses/merge/#query-merge-node-derived)';
324+
}
325+
} else if (mutationAction === NodeMutation.DELETE) {
326+
if (primaryKey) {
327+
mutationField = mutationConfig;
328+
mutationDescriptionUrl =
329+
'[deleting](https://neo4j.com/docs/cypher-manual/4.1/clauses/delete/#delete-delete-single-node)';
330+
}
331+
}
332+
if (mutationField) {
333+
mutationField.description = buildDescription({
334+
value: `[Generated mutation](${GRANDSTACK_DOCS_SCHEMA_AUGMENTATION}/#${mutationAction.toLowerCase()}) for ${mutationDescriptionUrl} a ${typeName} node.`,
335+
config
336+
});
337+
mutationFields.push(buildField(mutationField));
338+
}
339+
operationTypeMap[OperationType.MUTATION].fields = mutationFields;
340+
}
341+
return operationTypeMap;
342+
};
343+
261344
/**
262345
* Builds the AST definitions for directive instances used by
263346
* generated node Mutation fields of NodeMutation names

0 commit comments

Comments
 (0)