Last active
November 8, 2021 15:01
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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