Skip to content
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

Add filter types to schema #21

Merged
merged 4 commits into from
Aug 20, 2020
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
76 changes: 76 additions & 0 deletions engine/src/main/java/io/graphqlcrud/Filters.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.graphqlcrud;

import graphql.Scalars;
import graphql.schema.*;

public class Filters {

public static GraphQLInputObjectType.Builder pageInputBuilder() {
return GraphQLInputObjectType.newInputObject().name("PageRequest")
.field(GraphQLInputObjectField.newInputObjectField().name("limit").type(Scalars.GraphQLInt))
.field(GraphQLInputObjectField.newInputObjectField().name("offset").type(Scalars.GraphQLInt));
}

public static GraphQLInputObjectType.Builder stringInputBuilder() {
return GraphQLInputObjectType.newInputObject().name("StringInput")
.field(GraphQLInputObjectField.newInputObjectField().name("ne").type(Scalars.GraphQLString))
.field(GraphQLInputObjectField.newInputObjectField().name("eq").type(Scalars.GraphQLString))
.field(GraphQLInputObjectField.newInputObjectField().name("le").type(Scalars.GraphQLString))
.field(GraphQLInputObjectField.newInputObjectField().name("lt").type(Scalars.GraphQLString))
.field(GraphQLInputObjectField.newInputObjectField().name("ge").type(Scalars.GraphQLString))
.field(GraphQLInputObjectField.newInputObjectField().name("gt").type(Scalars.GraphQLString))
.field(GraphQLInputObjectField.newInputObjectField().name("in").type(GraphQLList.list(GraphQLNonNull.nonNull(Scalars.GraphQLString))))
.field(GraphQLInputObjectField.newInputObjectField().name("contains").type(Scalars.GraphQLString))
.field(GraphQLInputObjectField.newInputObjectField().name("startsWith").type(Scalars.GraphQLString))
.field(GraphQLInputObjectField.newInputObjectField().name("endsWith").type(Scalars.GraphQLString));

}

public static GraphQLInputObjectType.Builder idInputBuilder() {
return GraphQLInputObjectType.newInputObject().name("IDInput")
.field(GraphQLInputObjectField.newInputObjectField().name("ne").type(Scalars.GraphQLID))
.field(GraphQLInputObjectField.newInputObjectField().name("eq").type(Scalars.GraphQLID))
.field(GraphQLInputObjectField.newInputObjectField().name("in").type(GraphQLList.list(GraphQLNonNull.nonNull(Scalars.GraphQLID))));

}

public static GraphQLInputObjectType.Builder booleanInputBuilder() {
return GraphQLInputObjectType.newInputObject().name("BooleanInput")
.field(GraphQLInputObjectField.newInputObjectField().name("ne").type(Scalars.GraphQLBoolean))
.field(GraphQLInputObjectField.newInputObjectField().name("eq").type(Scalars.GraphQLBoolean));
}

public static GraphQLInputObjectType.Builder floatInputBuilder() {
return GraphQLInputObjectType.newInputObject().name("FloatInput")
.field(GraphQLInputObjectField.newInputObjectField().name("ne").type(Scalars.GraphQLFloat))
.field(GraphQLInputObjectField.newInputObjectField().name("eq").type(Scalars.GraphQLFloat))
.field(GraphQLInputObjectField.newInputObjectField().name("le").type(Scalars.GraphQLFloat))
.field(GraphQLInputObjectField.newInputObjectField().name("lt").type(Scalars.GraphQLFloat))
.field(GraphQLInputObjectField.newInputObjectField().name("ge").type(Scalars.GraphQLFloat))
.field(GraphQLInputObjectField.newInputObjectField().name("gt").type(Scalars.GraphQLFloat))
.field(GraphQLInputObjectField.newInputObjectField().name("in").type(GraphQLList.list(GraphQLNonNull.nonNull(Scalars.GraphQLFloat))));
}

public static GraphQLInputObjectType.Builder intInputBuilder() {
return GraphQLInputObjectType.newInputObject().name("IntInput")
.field(GraphQLInputObjectField.newInputObjectField().name("ne").type(Scalars.GraphQLInt))
.field(GraphQLInputObjectField.newInputObjectField().name("eq").type(Scalars.GraphQLInt))
.field(GraphQLInputObjectField.newInputObjectField().name("le").type(Scalars.GraphQLInt))
.field(GraphQLInputObjectField.newInputObjectField().name("lt").type(Scalars.GraphQLInt))
.field(GraphQLInputObjectField.newInputObjectField().name("ge").type(Scalars.GraphQLInt))
.field(GraphQLInputObjectField.newInputObjectField().name("gt").type(Scalars.GraphQLInt))
.field(GraphQLInputObjectField.newInputObjectField().name("in").type(GraphQLList.list(GraphQLNonNull.nonNull(Scalars.GraphQLInt))));
}

public static GraphQLInputObjectType.Builder orderByInputBuilder() {
return GraphQLInputObjectType.newInputObject().name("OrderByInput")
.field(GraphQLInputObjectField.newInputObjectField().name("field").type(GraphQLNonNull.nonNull(Scalars.GraphQLString)))
.field(GraphQLInputObjectField.newInputObjectField().name("order").type(GraphQLTypeReference.typeRef("SortDirectionEnum")).defaultValue("ASC"));
}

public static GraphQLEnumType.Builder sortDirectionEnumBuilder() {
return GraphQLEnumType.newEnum().name("SortDirectionEnum")
.value("ASC")
.value("DESC");
}
}
46 changes: 36 additions & 10 deletions engine/src/main/java/io/graphqlcrud/GraphQLSchemaBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,15 @@
import java.util.ArrayList;
import java.util.List;

import graphql.schema.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import graphql.Scalars;
import graphql.language.OperationTypeDefinition;
import graphql.language.SchemaDefinition;
import graphql.language.TypeName;
import graphql.schema.FieldCoordinates;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLNonNull;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLObjectType.Builder;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLTypeReference;
import io.graphqlcrud.model.Entity;
import io.graphqlcrud.model.Schema;
import io.graphqlcrud.types.JdbcTypeMap;
Expand All @@ -61,9 +53,28 @@ public GraphQLSchema buildSchema(Schema schema) {
GraphQLObjectType.Builder queryTypeBuilder = GraphQLObjectType.newObject();
queryTypeBuilder.name("QueryType");

//add filters for queries
builder.additionalType(Filters.pageInputBuilder().build());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. I think it will be amazing to use https://github.com/graphql-java/graphql-java/blob/master/src/main/java/graphql/schema/idl/SchemaPrinter.java to print schema for us to verify this under some different model use cases (we can use snapshot testing for this or anything else to validate correctness and call it a day.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are printing the schema using SchemaPrinter, once you run you can look for the schema.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running this now! thank you

builder.additionalType(Filters.stringInputBuilder().build());
builder.additionalType(Filters.intInputBuilder().build());
builder.additionalType(Filters.idInputBuilder().build());
builder.additionalType(Filters.booleanInputBuilder().build());
builder.additionalType(Filters.floatInputBuilder().build());
builder.additionalType(Filters.sortDirectionEnumBuilder().build());
builder.additionalType(Filters.orderByInputBuilder().build());

//add FilterInputTypes
schema.getEntities().stream().forEach(filterEntity -> {
GraphQLInputObjectType.Builder entityBuilder = GraphQLInputObjectType.newInputObject();
entityBuilder.name(StringUtil.capitalize(filterEntity.getName().toLowerCase()) + "FilterInput");
buildFilterFields(filterEntity,schema).stream().forEach(filter -> {
entityBuilder.field(filter.build());
});
builder.additionalType(entityBuilder.build());
});

schema.getEntities().stream().forEach(entity -> {
GraphQLObjectType.Builder typeBuilder = GraphQLObjectType.newObject();
//TODO : Model Annotations
typeBuilder.description(entity.getDescription());
typeBuilder.name(entity.getName());
typeBuilder.withDirective(SQLDirective.newDirective().tablename(entity.getFullName()).build());
Expand Down Expand Up @@ -101,6 +112,9 @@ private void addQueryOperationsForEntity(Entity entity, Builder queryTypeBuilder
String name = StringUtil.plural(entity.getName()).toLowerCase();
GraphQLFieldDefinition.Builder builder = GraphQLFieldDefinition.newFieldDefinition();
builder.name(name);
builder.argument(GraphQLArgument.newArgument().name("page").type(GraphQLTypeReference.typeRef("PageRequest")).build());
builder.argument(GraphQLArgument.newArgument().name("filter").type(GraphQLTypeReference.typeRef(StringUtil.capitalize(entity.getName().toLowerCase()) + "FilterInput")).build());
builder.argument(GraphQLArgument.newArgument().name("orderBy").type(GraphQLTypeReference.typeRef("OrderByInput")).build());
builder.type(GraphQLList.list(new GraphQLTypeReference(entity.getName())));
queryTypeBuilder.field(builder.build());
codeBuilder.dataFetcher(FieldCoordinates.coordinates("QueryType", name), DEFAULT_DATA_FETCHER_FACTORY);
Expand Down Expand Up @@ -166,4 +180,16 @@ protected List<GraphQLFieldDefinition.Builder> buildTypeFields(Entity entity, Sc
});
return fields;
}

protected List<GraphQLInputObjectField.Builder> buildFilterFields(Entity filterEntity, Schema schema) {
ArrayList<GraphQLInputObjectField.Builder> filterFields = new ArrayList<GraphQLInputObjectField.Builder>();

filterEntity.getAttributes().stream().forEach(attr -> {
GraphQLInputObjectField.Builder builder = GraphQLInputObjectField.newInputObjectField();
builder.name(attr.getName());
builder.type(TYPEMAP.getAsGraphQLFilterType(attr.getType()));
filterFields.add(builder);
});
return filterFields;
}
}
31 changes: 7 additions & 24 deletions engine/src/main/java/io/graphqlcrud/SQLDataFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,10 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import graphql.schema.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.teiid.language.AndOr;
import org.teiid.language.ColumnReference;
import org.teiid.language.Comparison;
import org.teiid.language.Condition;
import org.teiid.language.DerivedColumn;
import org.teiid.language.Expression;
import org.teiid.language.Join;
import org.teiid.language.Literal;
import org.teiid.language.NamedTable;
import org.teiid.language.Select;
import org.teiid.language.TableReference;
import org.teiid.language.*;
import org.teiid.language.visitor.SQLStringVisitor;

import graphql.language.Argument;
Expand All @@ -45,14 +36,6 @@
import graphql.language.Selection;
import graphql.language.StringValue;
import graphql.language.Value;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLModifiedType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeUtil;
import graphql.schema.SelectedField;

// This must be thread safe, as it will be called by multiple threads at same time
// This is very simple naively written class, will require more structure here
Expand Down Expand Up @@ -88,12 +71,12 @@ private String buildSQL(DataFetchingEnvironment environment) {
null, null);

// the parent query like "customers" definition
Field rootFeild = environment.getField();
Field rootField = environment.getField();
GraphQLFieldDefinition rootDefinition = environment.getFieldDefinition();
NamedTable table = buildTable(rootDefinition, inc);

// add select
TableReference from = buildSelect(environment, select, table, rootFeild, rootDefinition, null, inc);
TableReference from = buildSelect(environment, select, table, rootField, rootDefinition, null, inc);

// add from
select.getFrom().add(from);
Expand All @@ -104,7 +87,7 @@ private String buildSQL(DataFetchingEnvironment environment) {
}

private TableReference buildSelect(DataFetchingEnvironment environment, Select select, TableReference left,
Field rootFeild, GraphQLFieldDefinition rootFieldDefinition, String fqn, AtomicInteger inc) {
Field rootField, GraphQLFieldDefinition rootFieldDefinition, String fqn, AtomicInteger inc) {

NamedTable table = null;
if (left instanceof NamedTable) {
Expand All @@ -121,7 +104,7 @@ private TableReference buildSelect(DataFetchingEnvironment environment, Select s
}

if (GraphQLTypeUtil.isScalar(rootType)) {
select.getDerivedColumns().add(new DerivedColumn(null, new ColumnReference(table, rootFeild.getName(), null, null)));
select.getDerivedColumns().add(new DerivedColumn(null, new ColumnReference(table, rootField.getName(), null, null)));
} else if (rootType instanceof GraphQLObjectType) {
SQLDirective sqlDirective = SQLDirective.find(((GraphQLObjectType)rootType).getDirectives());
if (rootSqlDirective != null && rootSqlDirective.getPrimaryFields() != null) {
Expand All @@ -143,7 +126,7 @@ private TableReference buildSelect(DataFetchingEnvironment environment, Select s
}

// since this is object type loop through and selected fields
List<Selection> fields = rootFeild.getSelectionSet().getSelections();
List<Selection> fields = rootField.getSelectionSet().getSelections();
for (int i = 0; i < fields.size(); i++) {
Field f = (Field)fields.get(i);
String fieldName = fqn == null ? f.getName() : fqn+"/"+f.getName();
Expand Down
40 changes: 40 additions & 0 deletions engine/src/main/java/io/graphqlcrud/types/JdbcTypeMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@

import graphql.Scalars;
import graphql.scalars.ExtendedScalars;
import graphql.schema.GraphQLInputType;
import graphql.schema.GraphQLOutputType;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeReference;
import io.graphqlcrud.Filters;

public class JdbcTypeMap implements TypeMap {

Expand Down Expand Up @@ -66,4 +70,40 @@ public GraphQLOutputType getAsGraphQLTypeString(int dataType) {
}
return typeString;
}

@Override
public GraphQLInputType getAsGraphQLFilterType (int dataType) {
GraphQLInputType outputType;
switch (dataType) {
case Types.TINYINT:
case Types.INTEGER:
case Types.SMALLINT:
outputType = GraphQLTypeReference.typeRef("IntInput");
break;
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
outputType = GraphQLTypeReference.typeRef("StringInput");
break;
case Types.DOUBLE:
case Types.FLOAT:
case Types.REAL:
case Types.NUMERIC:
case Types.DECIMAL:
outputType = GraphQLTypeReference.typeRef("FloatInput");
break;
case Types.DATE:
case Types.TIMESTAMP:
case Types.TIME:
outputType = GraphQLTypeReference.typeRef("StringInput");
break;
case Types.BIT:
outputType = GraphQLTypeReference.typeRef("BooleanInput");
break;
default:
outputType = GraphQLTypeReference.typeRef("StringInput");
break;
}
return outputType;
}
}
3 changes: 3 additions & 0 deletions engine/src/main/java/io/graphqlcrud/types/TypeMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
*/
package io.graphqlcrud.types;

import graphql.schema.GraphQLInputType;
import graphql.schema.GraphQLOutputType;

public interface TypeMap {
GraphQLOutputType getAsGraphQLTypeString(int dataType);

GraphQLInputType getAsGraphQLFilterType(int dataType);
}
Loading