com.arcadedb.graphql.schema.GraphQLSchema Maven / Gradle / Ivy
The newest version!
/*
* Copyright © 2021-present Arcade Data Ltd ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: 2021-present Arcade Data Ltd ([email protected])
* SPDX-License-Identifier: Apache-2.0
*/
package com.arcadedb.graphql.schema;
import com.arcadedb.database.Database;
import com.arcadedb.exception.CommandParsingException;
import com.arcadedb.graphql.parser.Argument;
import com.arcadedb.graphql.parser.Arguments;
import com.arcadedb.graphql.parser.Definition;
import com.arcadedb.graphql.parser.Directive;
import com.arcadedb.graphql.parser.Directives;
import com.arcadedb.graphql.parser.Document;
import com.arcadedb.graphql.parser.Field;
import com.arcadedb.graphql.parser.FieldDefinition;
import com.arcadedb.graphql.parser.GraphQLParser;
import com.arcadedb.graphql.parser.InputValueDefinition;
import com.arcadedb.graphql.parser.ObjectTypeDefinition;
import com.arcadedb.graphql.parser.OperationDefinition;
import com.arcadedb.graphql.parser.ParseException;
import com.arcadedb.graphql.parser.Selection;
import com.arcadedb.graphql.parser.SelectionSet;
import com.arcadedb.graphql.parser.TypeDefinition;
import com.arcadedb.graphql.parser.TypeSystemDefinition;
import com.arcadedb.query.sql.executor.InternalResultSet;
import com.arcadedb.query.sql.executor.ResultSet;
import java.util.*;
public class GraphQLSchema {
private final Database database;
private final Map objectTypeDefinitionMap = new HashMap<>();
private ObjectTypeDefinition queryDefinition;
public GraphQLSchema(final Database database) {
this.database = database;
}
public ResultSet execute(final String query) throws ParseException {
final Document ast = GraphQLParser.parse(query);
final List definitions = ast.getDefinitions();
if (!definitions.isEmpty()) {
for (final Definition definition : definitions) {
if (definition instanceof TypeSystemDefinition) {
final TypeSystemDefinition typeSystemDefinition = (TypeSystemDefinition) definition;
final TypeDefinition type = typeSystemDefinition.getTypeDefinition();
if (type instanceof ObjectTypeDefinition) {
final ObjectTypeDefinition obj = (ObjectTypeDefinition) type;
objectTypeDefinitionMap.put(obj.getName(), obj);
if ("Query".equals(obj.getName()))
// SPECIAL TYPE QUERY
queryDefinition = obj;
}
} else if (definition instanceof OperationDefinition) {
final OperationDefinition op = ((OperationDefinition) definition);
if (op.isQuery()) {
return executeQuery(op);
} else
throw new UnsupportedOperationException("GraphQL mutations not supported yet");
}
}
}
return new InternalResultSet();
}
private ResultSet executeQuery(final OperationDefinition op) {
String from = null;
SelectionSet projection = null;
ObjectTypeDefinition returnType = null;
FieldDefinition typeDefinition = null;
final Set typeArgumentNames = new HashSet<>();
if (op.getSelectionSet().getSelections().size() > 1)
throw new CommandParsingException("Error on executing multiple queries");
String queryName = null;
final Map arguments = null;
try {
final Selection selection = op.getSelectionSet().getSelections().get(0);
queryName = selection.getName();
if (queryDefinition != null) {
for (final FieldDefinition f : queryDefinition.getFieldDefinitions()) {
if (queryName.equals(f.getName())) {
typeDefinition = f;
returnType = getTypeFromField(f);
for (final InputValueDefinition d : f.getArgumentsDefinition().getInputValueDefinitions())
typeArgumentNames.add(d.getName().getValue());
if (returnType != null)
from = returnType.getName();
}
}
}
if (from == null)
throw new CommandParsingException("Target type not defined for GraphQL query '" + queryName + "'");
if (typeDefinition != null) {
final Directives directives = typeDefinition.getDirectives();
if (directives != null) {
for (final Directive directive : directives.getDirectives()) {
final String directiveName = directive.getName();
// TODO: REFACTOR THIS IN MULTIPLE PLUGGABLE CLASSES
if ("sql".equals(directiveName) ||
"gremlin".equals(directiveName) ||
"cypher".equals(directiveName))
return parseNativeQueryDirective(directiveName, directive, selection, returnType);
}
}
}
String where = "";
final Field field = selection.getField();
if (field != null) {
final Arguments queryArguments = field.getArguments();
if (queryArguments != null)
for (final Argument queryArgument : queryArguments.getList()) {
final String argName = queryArgument.getName();
if (!typeArgumentNames.contains(argName))
throw new CommandParsingException("Parameter '" + argName + "' not defined in query");
final Object argValue = queryArgument.getValueWithVariable().getValue().getValue();
if (where.length() > 0)
where += " and ";
if ("where".equals(argName)) {
where += argValue;
} else {
where += argName;
where += " = ";
if (argValue instanceof String)
where += "\"";
where += argValue;
if (argValue instanceof String)
where += "\"";
}
}
projection = field.getSelectionSet();
}
String query = "select from " + from;
if (!where.isEmpty())
query += " where " + where;
final ResultSet resultSet = arguments != null ? database.query("sql", query, arguments) : database.query("sql", query);
return new GraphQLResultSet(this, resultSet, projection != null ? projection.getSelections() : null, returnType);
} catch (final CommandParsingException e) {
throw e;
} catch (final Exception e) {
if (queryName != null)
throw new CommandParsingException("Error on executing GraphQL query '" + queryName + "'", e);
throw new CommandParsingException("Error on executing GraphQL query", e);
}
}
private GraphQLResultSet parseNativeQueryDirective(final String language, final Directive directive, final Selection selection, final ObjectTypeDefinition returnType) {
if (directive.getArguments() == null)
throw new CommandParsingException(language.toUpperCase(Locale.ENGLISH) + " directive has no `statement` argument");
String statement = null;
Map arguments = null;
SelectionSet projection = null;
for (final Argument argument : directive.getArguments().getList()) {
if ("statement".equals(argument.getName())) {
statement = argument.getValueWithVariable().getValue().getValue().toString();
final Field field = selection.getField();
if (field != null) {
arguments = getArguments(field.getArguments());
projection = field.getSelectionSet();
}
}
}
if (statement == null)
throw new CommandParsingException(language.toUpperCase() + " directive has no `statement` argument");
final ResultSet resultSet = arguments != null ? database.query(language, statement, arguments) : database.query(language, statement);
return new GraphQLResultSet(this, resultSet, projection != null ? projection.getSelections() : null, returnType);
}
private Map getArguments(final Arguments queryArguments) {
Map arguments = null;
if (queryArguments != null) {
for (final Argument queryArgument : queryArguments.getList()) {
final String argName = queryArgument.getName();
final Object argValue = queryArgument.getValueWithVariable().getValue().getValue();
if (arguments == null)
arguments = new HashMap<>();
arguments.put(argName, argValue);
}
}
return arguments;
}
public ObjectTypeDefinition getTypeFromField(final FieldDefinition fieldDefinition) {
ObjectTypeDefinition returnType = null;
if (fieldDefinition.getType().getTypeName() != null) {
final String returnTypeName = fieldDefinition.getType().getTypeName().getName();
returnType = objectTypeDefinitionMap.get(returnTypeName);
} else if (fieldDefinition.getType().getListType() != null) {
final String returnTypeName = fieldDefinition.getType().getListType().getType().getTypeName().getName();
returnType = objectTypeDefinitionMap.get(returnTypeName);
}
return returnType;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy