Skip to content

bugfix: query with embedded entity producing an exception #306

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

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@
package com.introproventures.graphql.jpa.query.schema.impl;

import static graphql.Scalars.GraphQLBoolean;
import static graphql.introspection.Introspection.DirectiveLocation.FIELD;
import static graphql.schema.GraphQLArgument.newArgument;
import static graphql.schema.GraphQLInputObjectField.newInputObjectField;
import static graphql.schema.GraphQLInputObjectType.newInputObject;
import static graphql.schema.GraphQLNonNull.nonNull;

import java.beans.Introspector;
import java.lang.reflect.AnnotatedElement;
Expand Down Expand Up @@ -49,7 +47,6 @@
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type;

import graphql.schema.GraphQLDirective;
import org.dataloader.MappedBatchLoaderWithContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -64,9 +61,7 @@
import com.introproventures.graphql.jpa.query.schema.impl.EntityIntrospector.EntityIntrospectionResult.AttributePropertyDescriptor;
import com.introproventures.graphql.jpa.query.schema.impl.PredicateFilter.Criteria;
import com.introproventures.graphql.jpa.query.schema.relay.GraphQLJpaRelayDataFetcher;

import graphql.Assert;
import graphql.Directives;
import graphql.Scalars;
import graphql.relay.Relay;
import graphql.schema.Coercing;
Expand All @@ -86,6 +81,7 @@
import graphql.schema.GraphQLTypeReference;
import graphql.schema.PropertyDataFetcher;


/**
* JPA specific schema builder implementation of {code #GraphQLSchemaBuilder} interface
*
Expand Down Expand Up @@ -136,7 +132,7 @@ public class GraphQLJpaSchemaBuilder implements GraphQLSchemaBuilder {
private int defaultFetchSize = 100;
private int defaultPageLimitSize = 100;
private boolean enableDefaultMaxResults = true;

private RestrictedKeysProvider restrictedKeysProvider = (entityDescriptor) -> Optional.of(Collections.emptyList());

private final Relay relay = new Relay();
Expand Down Expand Up @@ -505,7 +501,7 @@ private String resolveTypeName(ManagedType<?> managedType) {
String typeName="";

if (managedType instanceof EmbeddableType){
typeName = managedType.getJavaType().getSimpleName()+"EmbeddableType";
typeName = managedType.getJavaType().getSimpleName();
} else if (managedType instanceof EntityType) {
typeName = ((EntityType<?>)managedType).getName();
}
Expand All @@ -514,7 +510,14 @@ private String resolveTypeName(ManagedType<?> managedType) {
}

private GraphQLInputObjectType getWhereInputType(ManagedType<?> managedType) {
return inputObjectCache.computeIfAbsent(managedType, this::computeWhereInputType);
GraphQLInputObjectType type = inputObjectCache.get(managedType);
if (type == null) {
type = computeWhereInputType(managedType);
inputObjectCache.put(managedType, type);
return type;
}
return type;

}

private String resolveWhereInputTypeName(ManagedType<?> managedType) {
Expand Down Expand Up @@ -610,6 +613,11 @@ private GraphQLInputType getWhereAttributeType(Attribute<?,?> attribute) {
if(whereAttributesMap.containsKey(type))
return whereAttributesMap.get(type);

if (isEmbeddable(attribute)) {
EmbeddableType<?> embeddableType = (EmbeddableType<?>) ((SingularAttribute<?, ?>) attribute).getType();
return getWhereInputType(embeddableType);
}

GraphQLInputObjectType.Builder builder = GraphQLInputObjectType.newInputObject()
.name(type)
.description("Criteria expression specification of "+namingStrategy.singularize(attribute.getName())+" attribute in entity " + attribute.getDeclaringType().getJavaType())
Expand Down Expand Up @@ -786,7 +794,7 @@ else if (attribute.getJavaMember().getClass().isAssignableFrom(Field.class)
}

private GraphQLArgument getArgument(Attribute<?,?> attribute) {
GraphQLInputType type = getAttributeInputType(attribute);
GraphQLInputType type = getAttributeInputTypeForSearchByIdArg(attribute);
String description = getSchemaDescription(attribute);

return GraphQLArgument.newArgument()
Expand All @@ -796,25 +804,33 @@ private GraphQLArgument getArgument(Attribute<?,?> attribute) {
.build();
}

private GraphQLType getEmbeddableType(EmbeddableType<?> embeddableType, boolean input) {
if (input && embeddableInputCache.containsKey(embeddableType.getJavaType()))
return embeddableInputCache.get(embeddableType.getJavaType());

if (!input && embeddableOutputCache.containsKey(embeddableType.getJavaType()))
return embeddableOutputCache.get(embeddableType.getJavaType());
String embeddableTypeName = namingStrategy.singularize(embeddableType.getJavaType().getSimpleName())+ (input ? "Input" : "") +"EmbeddableType";
GraphQLType graphQLType=null;
private GraphQLType getEmbeddableType(EmbeddableType<?> embeddableType, boolean input, boolean searchByIdArg) {
GraphQLType graphQLType;
if (input) {
graphQLType = GraphQLInputObjectType.newInputObject()
.name(embeddableTypeName)

if (searchByIdArg) {
if (embeddableInputCache.containsKey(embeddableType.getJavaType())) {
return embeddableInputCache.get(embeddableType.getJavaType());
}
graphQLType = GraphQLInputObjectType.newInputObject()
.name(namingStrategy.singularize(embeddableType.getJavaType().getSimpleName())+ "InputEmbeddableIdType")
.description(getSchemaDescription(embeddableType))
.fields(embeddableType.getAttributes().stream()
.filter(this::isNotIgnored)
.map(this::getInputObjectField)
.collect(Collectors.toList())
.filter(this::isNotIgnored)
.map(this::getInputObjectField)
.collect(Collectors.toList())
)
.build();
embeddableInputCache.put(embeddableType.getJavaType(), (GraphQLInputObjectType) graphQLType);
return graphQLType;
}

graphQLType = getWhereInputType(embeddableType);
} else {
if (embeddableOutputCache.containsKey(embeddableType.getJavaType())) {
return embeddableOutputCache.get(embeddableType.getJavaType());
}
String embeddableTypeName = namingStrategy.singularize(embeddableType.getJavaType().getSimpleName()) + "EmbeddableType";
graphQLType = GraphQLObjectType.newObject()
.name(embeddableTypeName)
.description(getSchemaDescription(embeddableType))
Expand All @@ -824,13 +840,8 @@ private GraphQLType getEmbeddableType(EmbeddableType<?> embeddableType, boolean
.collect(Collectors.toList())
)
.build();
}
if (input) {
embeddableInputCache.putIfAbsent(embeddableType.getJavaType(), (GraphQLInputObjectType) graphQLType);
} else{
embeddableOutputCache.putIfAbsent(embeddableType.getJavaType(), (GraphQLObjectType) graphQLType);
}

return graphQLType;
}

Expand Down Expand Up @@ -1020,31 +1031,39 @@ private Stream<Attribute<?,?>> findBasicAttributes(Collection<Attribute<?,?>> at
}

private GraphQLInputType getAttributeInputType(Attribute<?,?> attribute) {
return getAttributeInputType(attribute, false);
}

private GraphQLInputType getAttributeInputTypeForSearchByIdArg(Attribute<?,?> attribute) {
return getAttributeInputType(attribute, true);
}

private GraphQLInputType getAttributeInputType(Attribute<?,?> attribute, boolean searchByIdArgType) {

try {
return (GraphQLInputType) getAttributeType(attribute, true);
return (GraphQLInputType) getAttributeType(attribute, true, searchByIdArgType);
} catch (ClassCastException e){
throw new IllegalArgumentException("Attribute " + attribute + " cannot be mapped as an Input Argument");
}
}

private GraphQLOutputType getAttributeOutputType(Attribute<?,?> attribute) {
try {
return (GraphQLOutputType) getAttributeType(attribute, false);
return (GraphQLOutputType) getAttributeType(attribute, false, false);
} catch (ClassCastException e) {
throw new IllegalArgumentException("Attribute " + attribute + " cannot be mapped as an Output Argument");
}
}

@SuppressWarnings( "rawtypes" )
protected GraphQLType getAttributeType(Attribute<?,?> attribute, boolean input) {
protected GraphQLType getAttributeType(Attribute<?,?> attribute, boolean input, boolean searchByIdArgType) {

if (isBasic(attribute)) {
return getGraphQLTypeFromJavaType(attribute.getJavaType());
}
else if (isEmbeddable(attribute)) {
EmbeddableType embeddableType = (EmbeddableType) ((SingularAttribute) attribute).getType();
return getEmbeddableType(embeddableType, input);
return getEmbeddableType(embeddableType, input, searchByIdArgType);
}
else if (isToMany(attribute)) {
EntityType foreignType = (EntityType) ((PluralAttribute) attribute).getElementType();
Expand All @@ -1066,8 +1085,7 @@ else if (isElementCollection(attribute)) {
}
else if (foreignType.getPersistenceType() == Type.PersistenceType.EMBEDDABLE) {
EmbeddableType embeddableType = EmbeddableType.class.cast(foreignType);
GraphQLType graphQLType = getEmbeddableType(embeddableType,
input);
GraphQLType graphQLType = getEmbeddableType(embeddableType, input, searchByIdArgType);

return input ? graphQLType : new GraphQLList(graphQLType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,14 @@ public void queryBooksWithyWithEmbeddedId() {
}

@Test
public void queryBooksWithyWithEmbeddedIdWhereCriteriaExpression() {
public void queryBooksWithWithEmbeddedIdWhereCriteriaExpression() {
//given
String query = "query {" +
" Books( " +
" where: {" +
" bookId: {" +
" EQ: {" +
" title: \"War and Piece\"" +
" language: \"Russian\"" +
" }" +
" title: {LIKE: \"War % Piece\"}" +
" language: {EQ: \"Russian\"}" +
" }" +
" }" +
" ){" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,20 @@ public void queryForEntityWithMappedSuperclass() {
assertThat(result.toString()).isEqualTo(expected);
}

@Test
public void whereQueryWithSeparateEmbeddedEntityFieldsCriterias() {
//given
String query = "query { Cars(where: { engine: {identification:{LIKE:\"%\"}, hp:{GE:200}} }) { select {id brand} } }";

String expected = "{Cars={select=[{id=2, brand=Cadillac}]}}";

//when
Object result = executor.execute(query).getData();

// then
assertThat(result.toString()).isEqualTo(expected);
}

// https://github.com/introproventures/graphql-jpa-query/issues/30
@Test
public void queryForEntityWithEmbeddedIdAndEmbeddedField() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
public class Engine {

String identification;
Long hp;

}
8 changes: 4 additions & 4 deletions graphql-jpa-query-schema/src/test/resources/data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ insert into book_publishers(book_id, name, country) values
(2, 'Willey', 'US'), (2, 'Simon', 'EU');

-- Car
insert into Car (id, brand, identification) values
(1, 'Ford', 'xxxxx'),
(2, 'Cadillac', 'yyyyy'),
(3, 'Toyota', 'zzzzz');
insert into Car (id, brand, identification, hp) values
(1, 'Ford', 'xxxxx', 160),
(2, 'Cadillac', 'yyyyy', 250),
(3, 'Toyota', 'zzzzz', 180);


-- Boat
Expand Down