import { FieldNode , IntValueNode , SelectionNode } from "graphql/language" ; import * as RDF from "@rdfjs/types" ; import { Algebra , Util as AlgebraUtil } from "sparqlalgebrajs" ; import { IConvertContext , SingularizeState } from "../IConvertContext" ; import { IConvertSettings } from "../IConvertSettings" ; import { Util } from "../Util" ; import { INodeQuadContext , NodeHandlerAdapter } from "./NodeHandlerAdapter" ; /**
 * A handler for converting GraphQL selection nodes to operations.
 */ export abstract class NodeHandlerSelectionAdapter < T extends SelectionNode > extends NodeHandlerAdapter < T > { constructor ( targetKind , util , settings ) { super ( targetKind , util , settings ) ; } param { FieldNode } field param { string } fieldLabel param { IConvertContext } convertContext return { INodeQuadContext } public getNodeQuadContextFieldNode ( field , fieldLabel , convertContext ) { return this . getNodeQuadContextSelectionSet ( field . selectionSet , fieldLabel , { ... convertContext , path : this . util . appendFieldToPath ( convertContext . path , fieldLabel ) , } ) ; } param { IConvertContext } convertContext param { FieldNode } fieldNode param { boolean } pushTerminalVariables param { Pattern [ ] } auxiliaryPatterns return { Operation } public fieldToOperation ( convertContext , fieldNode , pushTerminalVariables , auxiliaryPatterns ? ) { const nesting = pushTerminalVariables ; let offset = 0 ; let limit ; if ( fieldNode . name . value === 'id' || fieldNode . name . value === 'graph' ) { pushTerminalVariables = false ; if ( fieldNode . arguments ) { for ( const argument of fieldNode . arguments ) { if ( argument . name . value === '_' ) { this . util . handleNodeValue ( argument . value , fieldNode . name . value , convertContext ) ; } } } } const fieldLabel = this . util . getFieldLabel ( fieldNode ) ; if ( convertContext . singularizeState === SingularizeState . SINGLE ) { convertContext . singularizeVariables ! [ this . util . nameToVariable ( fieldLabel , convertContext ) . value ] = true ; } if ( pushTerminalVariables ) { const operationOverride = this . handleMetaField ( convertContext , fieldLabel , auxiliaryPatterns ) ; if ( operationOverride ) { return operationOverride ; } } const operations = auxiliaryPatterns ? [ this . util . operationFactory . createBgp ( auxiliaryPatterns ) ] : [ ] ; const subjectOutput = this . getNodeQuadContextFieldNode ( fieldNode , fieldLabel , convertContext ) ; let object = subjectOutput . subject || this . util . nameToVariable ( fieldLabel , convertContext ) ; let graph = subjectOutput . graph || convertContext . graph ; if ( subjectOutput . auxiliaryPatterns ) { operations . push ( this . util . operationFactory . createBgp ( subjectOutput . auxiliaryPatterns ) ) ; } let createQuadPattern = true ; let overrideObjectTerms = null ; if ( pushTerminalVariables && fieldNode . arguments && fieldNode . arguments . length ) { for ( const argument of fieldNode . arguments ) { if ( argument . name . value === '_' ) { const valueOutput = this . util . handleNodeValue ( argument . value , fieldNode . name . value , convertContext ) ; overrideObjectTerms = valueOutput . terms ; operations . push ( this . util . operationFactory . createBgp ( valueOutput . terms . map ( ( term ) => this . util . createQuadPattern ( convertContext . subject , fieldNode . name , term , convertContext . graph , convertContext . context ) ) ) ) ; if ( valueOutput . auxiliaryPatterns ) { operations . push ( this . util . operationFactory . createBgp ( valueOutput . auxiliaryPatterns ) ) ; } pushTerminalVariables = false ; break ; } else if ( argument . name . value === 'graph' ) { const valueOutput = this . util . handleNodeValue ( argument . value , fieldNode . name . value , convertContext ) ; if ( valueOutput . terms . length !== 1 ) { throw new Error ( `Only single values can be set as graph, but got ${ valueOutput . terms . length } at ${ fieldNode . name . value }` ) ; } graph = valueOutput . terms [ 0 ] ; convertContext = { ... convertContext , graph } ; if ( valueOutput . auxiliaryPatterns ) { operations . push ( this . util . operationFactory . createBgp ( valueOutput . auxiliaryPatterns ) ) ; } break ; } else if ( argument . name . value === 'alt' ) { let pathValue = argument . value ; if ( pathValue . kind !== 'ListValue' ) { pathValue = { kind : 'ListValue' , values : [ pathValue ] } ; } operations . push ( this . util . createQuadPath ( convertContext . subject , fieldNode . name , pathValue , object , convertContext . graph , convertContext . context ) ) ; createQuadPattern = false ; break ; } } } if ( pushTerminalVariables && createQuadPattern ) { operations . push ( this . util . operationFactory . createBgp ( [ this . util . createQuadPattern ( convertContext . subject , fieldNode . name , object , convertContext . graph , convertContext . context ) , ] ) ) ; } if ( fieldNode . arguments && fieldNode . arguments . length ) { for ( const argument of fieldNode . arguments ) { if ( argument . name . value === '_' || argument . name . value === 'graph' || argument . name . value === 'alt' ) { } else if ( argument . name . value === 'first' ) { if ( argument . value . kind !== 'IntValue' ) { throw new Error ( 'Invalid value type for \'first\' argument: ' + argument . value . kind ) ; } limit = parseInt ( ( < IntValueNode > argument . value ) . value , 10 ) ; } else if ( argument . name . value === 'offset' ) { if ( argument . value . kind !== 'IntValue' ) { throw new Error ( 'Invalid value type for \'offset\' argument: ' + argument . value . kind ) ; } offset = parseInt ( ( < IntValueNode > argument . value ) . value , 10 ) ; } else { const valueOutput = this . util . handleNodeValue ( argument . value , argument . name . value , convertContext ) ; operations . push ( this . util . operationFactory . createBgp ( valueOutput . terms . map ( ( term ) => this . util . createQuadPattern ( object , argument . name , term , convertContext . graph , convertContext . context ) ) ) ) ; if ( valueOutput . auxiliaryPatterns ) { operations . push ( this . util . operationFactory . createBgp ( valueOutput . auxiliaryPatterns ) ) ; } } } } const directiveOutputs = this . getDirectiveOutputs ( fieldNode . directives , fieldLabel , convertContext ) ; if ( ! directiveOutputs ) { return this . util . operationFactory . createBgp ( [ ] ) ; } let operation = this . util . joinOperations ( operations ) ; if ( fieldNode . selectionSet && fieldNode . selectionSet . selections . length ) { if ( overrideObjectTerms ) { if ( overrideObjectTerms . length !== 1 ) { throw new Error ( `Only single values can be set as id, but got ${ overrideObjectTerms . length } at ${ fieldNode . name . value }` ) ; } object = overrideObjectTerms [ 0 ] ; } const subConvertContext = { ... convertContext , ... nesting ? { path : this . util . appendFieldToPath ( convertContext . path , fieldLabel ) } : { } , graph , subject : nesting ? object : convertContext . subject , } ; let totalCount = false ; const selections = fieldNode . selectionSet . selections . filter ( ( selection ) => { if ( selection . kind === 'Field' && selection . name . value === 'totalCount' ) { totalCount = true ; return false ; } return true ; } ) ; let joinedOperation = this . util . joinOperations ( operations . concat ( selections . map ( ( selectionNode ) => this . util . handleNode ( selectionNode , subConvertContext ) ) ) ) ; if ( totalCount ) { const expressionVariable = this . util . dataFactory . variable ! ( 'var' + this . settings . expressionVariableCounter ! ++ ) ; const countOverVariable = this . util . dataFactory . variable ! ( object . value + this . settings . variableDelimiter + 'totalCount' ) ; const aggregator = this . util . operationFactory . createBoundAggregate ( expressionVariable , 'count' , this . util . operationFactory . createTermExpression ( object ) , false ) ; const countProject = this . util . operationFactory . createProject ( this . util . operationFactory . createExtend ( this . util . operationFactory . createGroup ( operation , [ ] , [ aggregator ] ) , countOverVariable , this . util . operationFactory . createTermExpression ( expressionVariable ) ) , [ countOverVariable ] ) ; convertContext . terminalVariables . push ( countOverVariable ) ; if ( ! selections . length ) { joinedOperation = countProject ; } else { joinedOperation = this . util . operationFactory . createJoin ( [ this . util . operationFactory . createProject ( joinedOperation , [ ] ) , countProject , ] ) ; } } operation = joinedOperation ; } else if ( pushTerminalVariables && object . termType === 'Variable' ) { convertContext . terminalVariables . push ( object ) ; } if ( offset || limit ) { operation = this . util . operationFactory . createSlice ( this . util . operationFactory . createProject ( operation , AlgebraUtil . inScopeVariables ( operation ) ) , offset , limit ) ; } return this . handleDirectiveOutputs ( directiveOutputs , operation ) ; } param { IConvertContext } convertContext param { Term } subject param { string } fieldLabel param { Pattern [ ] } auxiliaryPatterns return { Operation } public handleMetaField ( convertContext , fieldLabel , auxiliaryPatterns ? ) { if ( fieldLabel === '__typename' ) { const object = this . util . nameToVariable ( fieldLabel , convertContext ) ; convertContext . terminalVariables . push ( object ) ; return this . util . operationFactory . createBgp ( [ this . util . operationFactory . createPattern ( convertContext . subject , this . util . dataFactory . namedNode ( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' ) , this . util . nameToVariable ( fieldLabel , convertContext ) , convertContext . graph ) , ] . concat ( auxiliaryPatterns || [ ] ) ) ; } } }