Skip to content

Instantly share code, notes, and snippets.

@sim51
Last active November 8, 2021 15:01
Show Gist options
  • Save sim51/0ec87ae5f3992fc4e557139411796ddf to your computer and use it in GitHub Desktop.
Save sim51/0ec87ae5f3992fc4e557139411796ddf to your computer and use it in GitHub Desktop.
Translates a graphql query into a neo4j/graphql query using neo4j/graphql-ogm
import { GraphQLResolveInfo, FieldNode, SelectionNode, SelectionSetNode, print } from "graphql";
import { ResolverContext } from "./index";
/**
*
* Translates a graphql query into a neo4j/graphql query using neo4j/graphql-ogm
* @see https://github.com/neo4j/graphql/issues/227
*
* @param args graphql query args
* @param info GraphQLResolveInfo
*/
export async function runNeo4jGqlQuery<T>(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
args: { where?: any; options?: any },
context: ResolverContext,
info: GraphQLResolveInfo,
custom: { label: string; subField: string } | null = null,
): Promise<Array<T>> {
const nodeLabel = custom !== null ? custom.label : info.fieldName;
const type = context.ogm.model(nodeLabel);
const rootSelectionSet =
custom !== null
? getSubFieldSelectionSet(custom.subField, info.operation.selectionSet)
: info.operation.selectionSet;
if (rootSelectionSet !== null) {
const operationStr = resolveOperationStrWithFragments(info, print(rootSelectionSet));
const parentHasArgs = Object.keys(args).length > 0;
const where = parentHasArgs && args.where;
const options = parentHasArgs && args.options;
const selectionSet = !parentHasArgs
? operationStr.split(nodeLabel.toLowerCase() + "s")[1].slice(0, -1)
: operationStr.substring(operationStr.indexOf(")") + 1).slice(0, -1);
return await type.find({ where, selectionSet: custom ? operationStr : selectionSet, options });
}
return [];
}
/**
* Retrieve the GraphQL selectNode that match the given field.
*/
function getSubFieldSelectionSet(field: string, node: SelectionSetNode): SelectionSetNode | null {
let result: SelectionSetNode | null = null;
node.selections.forEach((selection: SelectionNode) => {
if (result === null && Object.prototype.hasOwnProperty.call(selection, "selectionSet")) {
const castedSeclection = selection as FieldNode;
if (castedSeclection.name.kind === "Name" && castedSeclection.name.value === field) {
result = castedSeclection.selectionSet || null;
} else {
if (castedSeclection.selectionSet) {
result = getSubFieldSelectionSet(field, castedSeclection.selectionSet);
}
}
}
});
return result;
}
/**
* Replace fragments by their value for the operation.
* This operation is recursive because a fragment can use a fragment.
*/
function resolveOperationStrWithFragments(info: GraphQLResolveInfo, operation: string): string {
let op = operation;
let shouldContinue = true;
let index = 0;
while (shouldContinue && index < 5) {
op = Object.entries(info.fragments).reduce((operationStr, fragment) => {
return operationStr.replace(
`...${fragment[0]}\n`,
`${print(info.fragments[fragment[0]].selectionSet).substring(1).slice(0, -1)}\n`,
);
}, op);
index++;
shouldContinue = Object.entries(info.fragments).reduce((acc: boolean, fragment) => {
if (op.includes(`...${fragment[0]}\n`)) return true;
else return acc;
}, false);
}
return op;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment