Skip to content

Feat/relationship-properties-create #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/graphql/src/classes/Relationship.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface RelationshipConstructor {
name: string;
type: string;
description?: string;
properties?: string;
fields: RelationshipField[];
}

Expand All @@ -41,12 +42,15 @@ class Relationship {

public description?: string;

public properties?: string;

public fields: RelationshipField[];

constructor(input: RelationshipConstructor) {
this.name = input.name;
this.type = input.type;
this.description = input.description;
this.properties = input.properties;
this.fields = input.fields;
}
}
Expand Down
65 changes: 57 additions & 8 deletions packages/graphql/src/schema/make-augmented-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,28 @@ function makeAugmentedSchema(
name: `${relationship.name.value}Where`,
fields: relationshipWhereFields,
});

composer.createInputTC({
name: `${relationship.name.value}CreateInput`,
// TODO - Duplicated with rel properties
fields: relationshipFieldMeta.reduce((res, f) => {
if ((f as PrimitiveField)?.autogenerate) {
return res;
}

if ((f as PrimitiveField)?.defaultValue !== undefined) {
const field: InputTypeComposerFieldConfigAsObjectDefinition = {
type: f.typeMeta.input.create.pretty,
defaultValue: (f as PrimitiveField)?.defaultValue,
};
res[f.fieldName] = field;
} else {
res[f.fieldName] = f.typeMeta.input.create.pretty;
}

return res;
}, {}),
});
});

if (pointInTypeDefs) {
Expand Down Expand Up @@ -363,6 +385,7 @@ function makeAugmentedSchema(
});
}

// TODO - use getWhereFields
const queryFields = {
OR: `[${node.name}Where!]`,
AND: `[${node.name}Where!]`,
Expand Down Expand Up @@ -434,6 +457,7 @@ function makeAugmentedSchema(

const nodeInput = composer.createInputTC({
name: `${node.name}CreateInput`,
// TODO - Duplicated with rel properties
fields: [
...node.primitiveFields,
...node.scalarFields,
Expand Down Expand Up @@ -550,7 +574,9 @@ function makeAugmentedSchema(

refNodes.forEach((n) => {
const concatFieldName = `${rel.fieldName}_${n.name}`;
const createField = rel.typeMeta.array ? `[${n.name}CreateInput!]` : `${n.name}CreateInput`;
const createFieldInputName = rel.typeMeta.array
? `[${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput!]`
: `${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput`;
const updateField = `${n.name}UpdateInput`;
const nodeFieldInputName = `${node.name}${upperFirstLetter(rel.fieldName)}${n.name}FieldInput`;
const nodeFieldUpdateInputName = `${node.name}${upperFirstLetter(rel.fieldName)}${
Expand All @@ -574,22 +600,32 @@ function makeAugmentedSchema(
[n.name]: `${n.name}Where`,
});

if (!composer.has(`${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput`)) {
composer.createInputTC({
name: `${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput`,
fields: {
node: `${n.name}CreateInput!`,
...(rel.properties ? { properties: `${rel.properties}CreateInput!` } : {}),
},
});
}

composer.createInputTC({
name: nodeFieldUpdateInputName,
fields: {
where: `${n.name}Where`,
update: updateField,
connect: connectField,
disconnect: disconnectField,
create: createField,
create: createFieldInputName,
delete: deleteField,
},
});

composer.createInputTC({
name: nodeFieldInputName,
fields: {
create: createField,
create: createFieldInputName,
connect: connectField,
},
});
Expand All @@ -607,7 +643,7 @@ function makeAugmentedSchema(
});

nodeRelationInput.addFields({
[concatFieldName]: createField,
[concatFieldName]: createFieldInputName,
});

nodeInput.addFields({
Expand Down Expand Up @@ -639,9 +675,11 @@ function makeAugmentedSchema(
}

const n = nodes.find((x) => x.name === rel.typeMeta.name) as Node;
const createField = rel.typeMeta.array ? `[${n.name}CreateInput!]` : `${n.name}CreateInput`;
const updateField = `${n.name}UpdateInput`;
const nodeFieldInputName = `${node.name}${upperFirstLetter(rel.fieldName)}FieldInput`;
const createFieldInputName = rel.typeMeta.array
? `[${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput!]`
: `${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput`;
const nodeFieldUpdateInputName = `${node.name}${upperFirstLetter(rel.fieldName)}UpdateFieldInput`;
const nodeFieldDeleteInputName = `${node.name}${upperFirstLetter(rel.fieldName)}DeleteFieldInput`;
const connectField = rel.typeMeta.array ? `[${n.name}ConnectFieldInput!]` : `${n.name}ConnectFieldInput`;
Expand All @@ -660,6 +698,16 @@ function makeAugmentedSchema(
}),
});

if (!composer.has(`${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput`)) {
composer.createInputTC({
name: `${node.name}${upperFirstLetter(rel.fieldName)}CreateFieldInput`,
fields: {
node: `${n.name}CreateInput!`,
...(rel.properties ? { properties: `${rel.properties}CreateInput!` } : {}),
},
});
}

composeNode.addFields({
[rel.fieldName]: {
type: rel.typeMeta.pretty,
Expand All @@ -677,15 +725,15 @@ function makeAugmentedSchema(
update: updateField,
connect: connectField,
disconnect: disconnectField,
create: createField,
create: createFieldInputName,
delete: deleteField,
},
});

composer.createInputTC({
name: nodeFieldInputName,
fields: {
create: createField,
create: createFieldInputName,
connect: connectField,
},
});
Expand All @@ -705,7 +753,7 @@ function makeAugmentedSchema(
}

nodeRelationInput.addFields({
[rel.fieldName]: createField,
[rel.fieldName]: createFieldInputName,
});

nodeInput.addFields({
Expand Down Expand Up @@ -823,6 +871,7 @@ function makeAugmentedSchema(
fields: connectionField.relationship.properties
? (relationshipFields.get(connectionField.relationship.properties) as RelationshipField[])
: [],
properties: connectionField.relationship.properties,
});
relationships.push(r);
});
Expand Down
40 changes: 31 additions & 9 deletions packages/graphql/src/translate/create-create-and-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
* limitations under the License.
*/

import { Node } from "../classes";
import { Node, Relationship } from "../classes";
import { Context } from "../types";
import createConnectAndParams from "./create-connect-and-params";
import createAuthAndParams from "./create-auth-and-params";
import { AUTH_FORBIDDEN_ERROR } from "../constants";
import createSetRelationshipProperties from "./create-set-relationship-properties";

interface Res {
creates: string[];
Expand Down Expand Up @@ -68,23 +69,41 @@ function createCreateAndParams({
if (value.create) {
const creates = relationField.typeMeta.array ? value.create : [value.create];
creates.forEach((create, index) => {
const innerVarName = `${_varName}${index}`;
res.creates.push(`\nWITH ${withVars.join(", ")}`);

const baseName = `${_varName}${index}`;
const nodeName = `${baseName}_node`;
const propertiesName = `${baseName}_relationship`;

const recurse = createCreateAndParams({
input: create,
input: create.node,
context,
node: refNode,
varName: innerVarName,
withVars: [...withVars, innerVarName],
varName: nodeName,
withVars: [...withVars, nodeName],
});
res.creates.push(recurse[0]);
res.params = { ...res.params, ...recurse[1] };

const inStr = relationField.direction === "IN" ? "<-" : "-";
const outStr = relationField.direction === "OUT" ? "->" : "-";
const relTypeStr = `[:${relationField.type}]`;
res.creates.push(`MERGE (${varName})${inStr}${relTypeStr}${outStr}(${innerVarName})`);
const relTypeStr = `[${create.properties ? propertiesName : ""}:${relationField.type}]`;
res.creates.push(`MERGE (${varName})${inStr}${relTypeStr}${outStr}(${nodeName})`);

if (create.properties) {
const relationship = (context.neoSchema.relationships.find(
(x) => x.properties === relationField.properties
) as unknown) as Relationship;

const setA = createSetRelationshipProperties({
properties: create.properties,
varName: propertiesName,
relationship,
operation: "CREATE",
});
res.creates.push(setA[0]);
res.params = { ...res.params, ...setA[1] };
}
});
}

Expand Down Expand Up @@ -132,10 +151,13 @@ function createCreateAndParams({
} else {
res.creates.push(`SET ${varName}.${key} = point($${_varName})`);
}
} else {
res.creates.push(`SET ${varName}.${key} = $${_varName}`);

res.params[_varName] = value;

return res;
}

res.creates.push(`SET ${varName}.${key} = $${_varName}`);
res.params[_varName] = value;

return res;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable prefer-destructuring */
import { Relationship } from "../classes";
import { BaseField, DateTimeField, PrimitiveField } from "../types";

/*
TODO - lets reuse this function for setting either node or rel properties.
This was not reused due to the large differences between node fields
- and relationship fields.
*/
function createSetRelationshipProperties({
properties,
varName,
relationship,
operation,
}: {
properties: Record<string, unknown>;
varName: string;
relationship: Relationship;
operation: "CREATE" | "UPDATE";
}): [string, any] {
const strs: string[] = [];
const params = {};

Object.entries(properties).forEach((entry) => {
const paramName = `${varName}_${entry[0]}`;
const field = (relationship.fields.find((x) => x.fieldName === entry[0]) as unknown) as BaseField;

if ("timestamps" in field) {
const f = field as DateTimeField;
(f.timestamps || []).forEach((ts) => {
if (ts.includes(operation)) {
strs.push(`SET ${varName}.${f.fieldName} = datetime()`);
}
});

return;
}

if ("autogenerate" in field) {
const f = field as PrimitiveField;

strs.push(`SET ${varName}.${f.fieldName} = randomUUID()`);

return;
}

if (["Point", "CartesianPoint"].includes(field.typeMeta.name)) {
if (field.typeMeta.array) {
strs.push(`SET ${varName}.${field.fieldName} = [p in $${paramName} | point(p)]`);
} else {
strs.push(`SET ${varName}.${field.fieldName} = point($${paramName})`);
}

params[paramName] = entry[1];

return;
}

strs.push(`SET ${varName}.${field.fieldName} = $${paramName}`);
params[paramName] = entry[1];
});

return [strs.join("\n"), params];
}

export default createSetRelationshipProperties;

/* eslint-enable prefer-destructuring */
Loading