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

Merge operation support #355

Merged
merged 12 commits into from
Dec 5, 2019
7 changes: 4 additions & 3 deletions example/apollo-server/movies-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ type OnlyDate {
}

type SpatialNode {
pointKey: Point
id: ID!
point: Point
spatialNodes(pointKey: Point): [SpatialNode]
spatialNodes(point: Point): [SpatialNode]
@relation(name: "SPATIAL", direction: OUT)
}

Expand Down Expand Up @@ -115,7 +115,8 @@ type Query {
MovieById(movieId: ID!): Movie
GenresBySubstring(substring: String): [Genre] @cypher(statement: "MATCH (g:Genre) WHERE toLower(g.name) CONTAINS toLower($substring) RETURN g")
Books: [Book]
}`;
}
`;

export const resolvers = {
// root entry point to GraphQL service
Expand Down
20 changes: 13 additions & 7 deletions src/augment/types/node/mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import { TypeWrappers, getFieldDefinition, isNeo4jIDField } from '../../fields';
/**
* An enum describing the names of node type mutations
*/
export const NodeMutation = {
const NodeMutation = {
CREATE: 'Create',
UPDATE: 'Update',
DELETE: 'Delete'
// MERGE: 'Merge'
DELETE: 'Delete',
MERGE: 'Merge'
};

/**
Expand Down Expand Up @@ -103,7 +103,10 @@ const buildNodeMutationField = ({
};
if (mutationAction === NodeMutation.CREATE) {
mutationFields.push(buildField(mutationField));
} else if (mutationAction === NodeMutation.UPDATE) {
} else if (
mutationAction === NodeMutation.UPDATE ||
mutationAction === NodeMutation.MERGE
) {
if (primaryKey && mutationField.args.length > 1) {
mutationFields.push(buildField(mutationField));
}
Expand Down Expand Up @@ -134,8 +137,8 @@ const buildNodeMutationArguments = ({
const type = field.type;
if (operationName === NodeMutation.CREATE) {
// Uses primary key and any other property field
if (primaryKeyName === field.name) {
if (field.type.name === GraphQLID.name) {
if (primaryKeyName === name) {
if (type.name === GraphQLID.name) {
// Create auto-generates ID primary keys
args.push({
name,
Expand All @@ -158,7 +161,10 @@ const buildNodeMutationArguments = ({
type
});
}
} else if (operationName === NodeMutation.UPDATE) {
} else if (
operationName === NodeMutation.UPDATE ||
operationName === NodeMutation.MERGE
) {
// Uses primary key and any other property field
if (primaryKeyName === name) {
// Require primary key otherwise
Expand Down
55 changes: 44 additions & 11 deletions src/augment/types/relationship/mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { RelationshipDirectionField } from './relationship';
import { buildNodeOutputFields } from './query';
import { shouldAugmentRelationshipField } from '../../augment';
import { OperationType } from '../../types/types';
import { TypeWrappers, getFieldDefinition } from '../../fields';
import {
TypeWrappers,
getFieldDefinition,
unwrapNamedType
} from '../../fields';
import {
DirectiveDefinition,
buildAuthScopeDirective,
Expand All @@ -20,15 +24,18 @@ import {
buildObjectType,
buildInputObjectType
} from '../../ast';
import { getPrimaryKey } from '../../../utils';

/**
* An enum describing the names of relationship mutations,
* for node and relationship type fields (field and type
* relation directive)
*/
export const RelationshipMutation = {
const RelationshipMutation = {
CREATE: 'Add',
DELETE: 'Remove'
DELETE: 'Remove',
UPDATE: 'Update',
MERGE: 'Merge'
};

/**
Expand Down Expand Up @@ -68,11 +75,19 @@ export const augmentRelationshipMutationAPI = ({
typeName,
fieldName
});
const fromTypeDefinition = typeDefinitionMap[fromType];
const toTypeDefinition = typeDefinitionMap[toType];
const fromTypePk = getPrimaryKey(fromTypeDefinition);
const toTypePk = getPrimaryKey(toTypeDefinition);
if (
!getFieldDefinition({
fields: mutationType.fields,
name: mutationName
})
}) &&
// Only generate mutation API for given relationship if both related
// nodes have a primary key
fromTypePk &&
toTypePk
) {
[operationTypeMap, generatedTypeMap] = buildRelationshipMutationAPI({
mutationAction,
Expand Down Expand Up @@ -149,6 +164,7 @@ const buildRelationshipMutationAPI = ({
relationshipName,
fromType,
toType,
propertyInputValues,
propertyOutputFields,
mutationOutputType,
outputType,
Expand Down Expand Up @@ -183,6 +199,7 @@ const buildRelationshipMutationField = ({
relationshipName,
fromType,
toType,
propertyInputValues,
propertyOutputFields,
mutationOutputType,
outputType,
Expand All @@ -191,7 +208,10 @@ const buildRelationshipMutationField = ({
}) => {
if (
mutationAction === RelationshipMutation.CREATE ||
mutationAction === RelationshipMutation.DELETE
mutationAction === RelationshipMutation.DELETE ||
(mutationAction === RelationshipMutation.UPDATE &&
propertyInputValues.length) ||
mutationAction === RelationshipMutation.MERGE
) {
operationTypeMap[OperationType.MUTATION].fields.push(
buildField({
Expand All @@ -213,7 +233,6 @@ const buildRelationshipMutationField = ({
relationshipName,
fromType,
toType,
propertyOutputFields,
config
})
})
Expand Down Expand Up @@ -252,7 +271,9 @@ const buildRelationshipMutationPropertyInputType = ({
generatedTypeMap
}) => {
if (
mutationAction === RelationshipMutation.CREATE &&
(mutationAction === RelationshipMutation.CREATE ||
mutationAction === RelationshipMutation.UPDATE ||
mutationAction === RelationshipMutation.MERGE) &&
propertyInputValues.length
) {
let nonComputedPropertyInputFields = propertyInputValues.filter(field => {
Expand Down Expand Up @@ -289,7 +310,9 @@ const buildRelationshipMutationArguments = ({
}) => {
const fieldArguments = buildNodeSelectionArguments({ fromType, toType });
if (
mutationAction === RelationshipMutation.CREATE &&
(mutationAction === RelationshipMutation.CREATE ||
mutationAction === RelationshipMutation.UPDATE ||
mutationAction === RelationshipMutation.MERGE) &&
propertyOutputFields.length
) {
fieldArguments.push(
Expand All @@ -311,7 +334,6 @@ const buildRelationshipMutationDirectives = ({
relationshipName,
fromType,
toType,
propertyOutputFields,
config
}) => {
const mutationMetaDirective = buildMutationMetaDirective({
Expand All @@ -326,6 +348,10 @@ const buildRelationshipMutationDirectives = ({
authAction = 'Create';
} else if (mutationAction === RelationshipMutation.DELETE) {
authAction = 'Delete';
} else if (mutationAction === RelationshipMutation.UPDATE) {
authAction = 'Update';
} else if (mutationAction === RelationshipMutation.MERGE) {
authAction = 'Merge';
}
if (authAction) {
directives.push(
Expand Down Expand Up @@ -362,15 +388,21 @@ const buildRelationshipMutationOutputType = ({
}) => {
if (
mutationAction === RelationshipMutation.CREATE ||
mutationAction === RelationshipMutation.DELETE
mutationAction === RelationshipMutation.DELETE ||
mutationAction === RelationshipMutation.MERGE ||
mutationAction === RelationshipMutation.UPDATE
) {
const relationTypeDirective = buildRelationDirective({
relationshipName,
fromType,
toType
});
let fields = buildNodeOutputFields({ fromType, toType });
if (mutationAction === RelationshipMutation.CREATE) {
if (
mutationAction === RelationshipMutation.CREATE ||
mutationAction === RelationshipMutation.UPDATE ||
mutationAction === RelationshipMutation.MERGE
) {
// TODO temporary block on cypher field arguments - needs translation test
const mutationOutputFields = propertyOutputFields.map(field => {
if (isCypherField({ directives: field.directives })) {
Expand All @@ -382,6 +414,7 @@ const buildRelationshipMutationOutputType = ({
});
fields.push(...mutationOutputFields);
}
// Overwrite
generatedTypeMap[mutationOutputType] = buildObjectType({
name: buildName({ name: mutationOutputType }),
fields,
Expand Down
Loading