From c261ec9d5dc9e683463e00abc6890fbb2b6d64f0 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Sat, 17 Aug 2019 07:24:21 -0700 Subject: [PATCH 1/4] feat: add support for variable where criteria expressions --- .../impl/QraphQLJpaBaseDataFetcher.java | 55 +++++++- .../query/schema/GraphQLExecutorTests.java | 124 +++++++++++++++++- 2 files changed, 167 insertions(+), 12 deletions(-) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java index 06ea2778c..0138aa8e7 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -57,10 +58,12 @@ import com.introproventures.graphql.jpa.query.annotation.GraphQLDefaultOrderBy; import com.introproventures.graphql.jpa.query.schema.impl.PredicateFilter.Criteria; + import graphql.GraphQLException; import graphql.execution.ValuesResolver; import graphql.language.Argument; import graphql.language.ArrayValue; +import graphql.language.AstValueHelper; import graphql.language.BooleanValue; import graphql.language.Comment; import graphql.language.EnumValue; @@ -79,6 +82,7 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.DataFetchingEnvironmentBuilder; +import graphql.schema.GraphQLArgument; import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLList; import graphql.schema.GraphQLObjectType; @@ -378,12 +382,29 @@ protected Predicate getPredicate(CriteriaBuilder cb, Root from, From pat @SuppressWarnings( "unchecked" ) - private R getValue(Argument argument) { - return (R) argument.getValue(); + private > R getValue(Argument argument, DataFetchingEnvironment environment) { + Value value = argument.getValue(); + + if(VariableReference.class.isInstance(value)) { + String variableName = VariableReference.class.cast(value) + .getName(); + + Object variableValue = environment.getExecutionContext() + .getVariables() + .get(variableName); + + GraphQLArgument graphQLArgument = environment.getExecutionStepInfo() + .getFieldDefinition() + .getArgument(variableName); + + return (R) AstValueHelper.astFromValue(variableValue, graphQLArgument.getType()); + } + + return (R) value; } protected Predicate getWherePredicate(CriteriaBuilder cb, Root root, From path, DataFetchingEnvironment environment, Argument argument) { - ObjectValue whereValue = getValue(argument); + ObjectValue whereValue = getValue(argument, environment); if(whereValue.getChildren().isEmpty()) return cb.conjunction(); @@ -404,7 +425,7 @@ protected Predicate getWherePredicate(CriteriaBuilder cb, Root root, From from, DataFetchingEnvironment environment, Argument argument) { - ObjectValue whereValue = getValue(argument); + ObjectValue whereValue = getValue(argument, environment); if (whereValue.getChildren().isEmpty()) return cb.disjunction(); @@ -494,7 +515,7 @@ protected Predicate getArgumentsPredicate(CriteriaBuilder cb, From path, DataFetchingEnvironment environment, Argument argument) { - ArrayValue whereValue = getValue(argument); + ArrayValue whereValue = getValue(argument, environment); if (whereValue.getValues().isEmpty()) return cb.disjunction(); @@ -897,7 +918,29 @@ else if (value instanceof VariableReference) { } } else if (value instanceof ArrayValue) { Object convertedValue = environment.getArgument(argument.getName()); - if (convertedValue != null && !getJavaType(environment, argument).isEnum()) { + + if (convertedValue != null && getJavaType(environment, argument).isEnum()) { + + Function f = (obj) -> Value.class.isInstance(obj) + ? Value.class.cast(obj) + : new EnumValue(obj.toString()); + + // unwrap [[EnumValue{name='value'}]] + if(convertedValue instanceof Collection + && ((Collection) convertedValue).stream().allMatch(it->it instanceof Collection)) { + convertedValue = ((Collection) convertedValue).iterator().next(); + } + + if(convertedValue instanceof Collection) { + return ((Collection) convertedValue).stream() + .map((it) -> convertValue(environment, argument, f.apply(it))) + .collect(Collectors.toList()); + } + // Return real typed resolved array value + return convertValue(environment, argument, f.apply(convertedValue)); + } + else + if (convertedValue != null && !getJavaType(environment, argument).isEnum()) { // unwrap [[EnumValue{name='value'}]] if(convertedValue instanceof Collection && ((Collection) convertedValue).stream().allMatch(it->it instanceof Collection)) { diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java index 6f92a7fcd..96a4586f5 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java @@ -18,19 +18,18 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.AbstractMap; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.persistence.EntityManager; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; -import graphql.ErrorType; -import graphql.ExecutionResult; -import graphql.GraphQLError; -import graphql.validation.ValidationError; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -42,6 +41,14 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.Assert; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; + +import graphql.ErrorType; +import graphql.ExecutionResult; +import graphql.GraphQLError; +import graphql.validation.ValidationError; + @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.NONE) @@ -1415,4 +1422,109 @@ public void queryForAuthorsWithExlicitOptionalBooksTrue() { // then assertThat(result.toString()).isEqualTo(expected); } + + @Test + public void queryBooksWithWhereVariableCriteriaExpression() { + //given + String query = "query($where: BooksCriteriaExpression) {" + + " Books (where: $where) {" + + " select {" + + " id" + + " title" + + " genre" + + " }" + + " }" + + "}"; + + Map variables = map(entry("where", + map(entry("title", + map(entry("LIKE", + "War")))))); + String expected = "{Books={select=[" + + "{id=2, title=War and Peace, genre=NOVEL}" + + "]}}"; + + //when + Object result = executor.execute(query, variables).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryBooksWithWhereVariableCriteriaEnumListExpression() { + //given + String query = "query($where: BooksCriteriaExpression) {" + + " Books (where: $where) {" + + " select {" + + " id" + + " title" + + " genre" + + " }" + + " }" + + "}"; + + Map variables = map(entry("where", + map(entry("genre", + map(entry("IN", + Arrays.asList("NOVEL"))))))); + + String expected = "{Books={select=[" + + "{id=2, title=War and Peace, genre=NOVEL}, " + + "{id=3, title=Anna Karenina, genre=NOVEL}" + + "]}}"; + + //when + Object result = executor.execute(query, variables).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryBooksWithWhereVariableCriteriaEnumExpression() { + //given + String query = "query($where: BooksCriteriaExpression) {" + + " Books (where: $where) {" + + " select {" + + " id" + + " title" + + " genre" + + " }" + + " }" + + "}"; + + Map variables = map(entry("where", + map(entry("genre", + map(entry("IN", + "NOVEL")))))); + String expected = "{Books={select=[" + + "{id=2, title=War and Peace, genre=NOVEL}, " + + "{id=3, title=Anna Karenina, genre=NOVEL}" + + "]}}"; + + //when + Object result = executor.execute(query, variables).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + + @SafeVarargs + static Map map(Entry... entries) { + if (entries.length == 0) { // implicit null check of entries array + return Collections.emptyMap(); + } else if (entries.length == 1) { + return Collections.singletonMap(entries[0].getKey(), entries[0].getValue()); + } else { + return Stream.of(entries) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + } + + static Entry entry(K k, V v) { + return new AbstractMap.SimpleImmutableEntry<>(k, v); + } + } \ No newline at end of file From 388851adef27a3b711a37ce3ceb59a39c72ae3b1 Mon Sep 17 00:00:00 2001 From: Mateusz Nowak Date: Mon, 19 Aug 2019 14:14:08 +0200 Subject: [PATCH 2/4] Add GraphQLWhereVariableBindingsTests and fix variable name mismatch issue (#164) --- .../impl/QraphQLJpaBaseDataFetcher.java | 14 +- .../GraphQLWhereVariableBindingsTests.java | 296 ++++++++++++++++++ 2 files changed, 303 insertions(+), 7 deletions(-) create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java index 0138aa8e7..6e9ab5bfe 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java @@ -395,7 +395,7 @@ private > R getValue(Argument argument, DataFetchingEnvironme GraphQLArgument graphQLArgument = environment.getExecutionStepInfo() .getFieldDefinition() - .getArgument(variableName); + .getArgument(argument.getName()); return (R) AstValueHelper.astFromValue(variableValue, graphQLArgument.getType()); } @@ -918,19 +918,19 @@ else if (value instanceof VariableReference) { } } else if (value instanceof ArrayValue) { Object convertedValue = environment.getArgument(argument.getName()); - + if (convertedValue != null && getJavaType(environment, argument).isEnum()) { - + Function f = (obj) -> Value.class.isInstance(obj) - ? Value.class.cast(obj) + ? Value.class.cast(obj) : new EnumValue(obj.toString()); - + // unwrap [[EnumValue{name='value'}]] if(convertedValue instanceof Collection && ((Collection) convertedValue).stream().allMatch(it->it instanceof Collection)) { convertedValue = ((Collection) convertedValue).iterator().next(); } - + if(convertedValue instanceof Collection) { return ((Collection) convertedValue).stream() .map((it) -> convertValue(environment, argument, f.apply(it))) @@ -939,7 +939,7 @@ else if (value instanceof VariableReference) { // Return real typed resolved array value return convertValue(environment, argument, f.apply(convertedValue)); } - else + else if (convertedValue != null && !getJavaType(environment, argument).isEnum()) { // unwrap [[EnumValue{name='value'}]] if(convertedValue instanceof Collection diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java new file mode 100644 index 000000000..d2a286fb3 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java @@ -0,0 +1,296 @@ +package com.introproventures.graphql.jpa.query.schema; + +import static org.assertj.core.api.Assertions.tuple; +import static org.assertj.core.api.BDDAssertions.then; + +import static com.introproventures.graphql.jpa.query.schema.model.book.Genre.NOVEL; +import static com.introproventures.graphql.jpa.query.schema.model.book.Genre.PLAY; + +import java.io.IOException; +import java.util.Map; + +import javax.persistence.EntityManager; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; + +import graphql.ExecutionResult; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.NONE) +@TestPropertySource({ "classpath:hibernate.properties" }) +public class GraphQLWhereVariableBindingsTests { + + @SpringBootApplication + static class Application { + + @Bean + public GraphQLExecutor graphQLExecutor(final GraphQLSchemaBuilder graphQLSchemaBuilder) { + return new GraphQLJpaExecutor(graphQLSchemaBuilder.build()); + } + + @Bean + public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManager entityManager) { + return new GraphQLJpaSchemaBuilder(entityManager) + .name("GraphQLBooks") + .description("Books JPA test schema"); + } + + } + + @Autowired + private GraphQLExecutor executor; + + @Test + public void queryWithSimpleEqualsVariableBinding() throws IOException { + //given + String query = "" + + "query($where: BooksCriteriaExpression) {" + + " Books(where: $where) {" + + " select {" + + " id" + + " title" + + " genre" + + " }" + + " }" + + "}"; + String variables = "" + + "{" + + " \"where\": {" + + " \"title\": {" + + " \"EQ\": \"War and Peace\"" + + " }" + + " }" + + "}"; + + //when + ExecutionResult executionResult = executor.execute(query, getVariablesMap(variables)); + + // then + then(executionResult.getErrors()).isEmpty(); + Map result = executionResult.getData(); + then(result) + .extracting("Books") + .flatExtracting("select") + .hasSize(1) + .extracting("id", "title", "genre") + .containsOnly(tuple(2L, "War and Peace", NOVEL)); + } + + @Test + public void queryWithNestedWhereClauseVariableBinding() throws IOException { + //given + String query = "" + + "query($where: BooksCriteriaExpression) {" + + " Books(where: $where) {" + + " select {" + + " author {" + + " id" + + " name" + + " }" + + " title" + + " }" + + " }" + + "}"; + String variables = "" + + "{" + + " \"where\": {" + + " \"author\": {" + + " \"name\": {" + + " \"EQ\": \"Leo Tolstoy\"" + + " }" + + " }" + + " }" + + "}"; + + //when + ExecutionResult executionResult = executor.execute(query, getVariablesMap(variables)); + + // then + then(executionResult.getErrors()).isEmpty(); + Map result = executionResult.getData(); + then(result) + .extracting("Books") + .flatExtracting("select") + .hasSize(2) + .extracting("title") + .containsOnly("War and Peace", "Anna Karenina"); + then(result) + .extracting("Books") + .flatExtracting("select") + .hasSize(2) + .extracting("author") + .extracting("id", "name") + .containsOnly(tuple(1L, "Leo Tolstoy")); + } + + @Test + public void queryWithInVariableBinding() throws IOException { + //given + String query = "" + + "query($where: BooksCriteriaExpression) {" + + " Books(where: $where) {" + + " select {" + + " id" + + " title" + + " genre" + + " }" + + " }" + + "}"; + String variables = "" + + "{" + + " \"where\": {" + + " \"genre\": {" + + " \"IN\": [\"PLAY\"]" + + " }" + + " }" + + "}"; + + //when + ExecutionResult executionResult = executor.execute(query, getVariablesMap(variables)); + + // then + then(executionResult.getErrors()).isEmpty(); + Map result = executionResult.getData(); + then(result) + .extracting("Books") + .flatExtracting("select") + .extracting("genre") + .containsOnly(PLAY); + } + + @Test + public void queryWithMultipleRestrictionForOneProperty() throws IOException { + //given + String query = "" + + "query($where: BooksCriteriaExpression) {" + + " Books(where: $where) {" + + " select {" + + " id" + + " title" + + " }" + + " }" + + "}"; + String variables = "" + + "{" + + " \"where\": {" + + " \"id\": {" + + " \"GE\": 5," + + " \"LE\": 7" + + " }" + + " }" + + "}"; + + //when + ExecutionResult executionResult = executor.execute(query, getVariablesMap(variables)); + + // then + then(executionResult.getErrors()).isEmpty(); + Map result = executionResult.getData(); + then(result) + .extracting("Books") + .flatExtracting("select") + .extracting("id", "title") + .containsOnly( + tuple(5L, "The Cherry Orchard"), + tuple(6L, "The Seagull"), + tuple(7L, "Three Sisters") + ); + } + + @Test + public void queryWithPropertyWhereVariableBinding() throws IOException { + //given + String query = "" + + "query($booksWhereClause: BooksCriteriaExpression) {" + + " Authors {" + + " select {" + + " name" + + " books(where: $booksWhereClause) {" + + " genre" + + " }" + + " }" + + " }" + + "}"; + String variables = "" + + "{" + + " \"booksWhereClause\": {" + + " \"genre\": {" + + " \"IN\": [\"NOVEL\"]" + + " }" + + " }" + + "}"; + + //when + ExecutionResult executionResult = executor.execute(query, getVariablesMap(variables)); + + // then + then(executionResult.getErrors()).isEmpty(); + Map result = executionResult.getData(); + then(result) + .extracting("Authors") + .flatExtracting("select") + .extracting("name") + .containsOnly("Leo Tolstoy"); + then(result) + .extracting("Authors") + .flatExtracting("select") + .flatExtracting("books") + .extracting("genre") + .containsOnly(NOVEL); + } + + @Test + public void queryWithRestrictionsForMultipleProperties() throws IOException { + //given + String query = "" + + "query($where: BooksCriteriaExpression) {" + + " Books(where: $where) {" + + " select {" + + " id" + + " title" + + " }" + + " }" + + "}"; + String variables = "" + + "{" + + " \"where\": {" + + " \"title\": {" + + " \"LIKE\": \"The\"" + + " }," + + " \"id\": {" + + " \"LT\": 6" + + " }" + + " }" + + "}"; + + //when + ExecutionResult executionResult = executor.execute(query, getVariablesMap(variables)); + + // then + then(executionResult.getErrors()).isEmpty(); + Map result = executionResult.getData(); + then(result) + .extracting("Books") + .flatExtracting("select") + .extracting("id", "title") + .containsOnly(tuple(5L, "The Cherry Orchard")); + } + + @SuppressWarnings("unchecked") + private Map getVariablesMap(String variables) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + return (Map) mapper.readValue(variables, Map.class); + } +} \ No newline at end of file From 9a5cd1b4b31ed3d3e297f2588b52c9af8755a9fd Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 19 Aug 2019 23:28:17 -0700 Subject: [PATCH 3/4] chore: consolidate tests for where variable bindings --- .../query/schema/GraphQLExecutorTests.java | 109 ------------------ .../GraphQLWhereVariableBindingsTests.java | 107 ++++++++++++++++- 2 files changed, 103 insertions(+), 113 deletions(-) diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java index 96a4586f5..cf980a069 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java @@ -18,15 +18,10 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.AbstractMap; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.persistence.EntityManager; @@ -1423,108 +1418,4 @@ public void queryForAuthorsWithExlicitOptionalBooksTrue() { assertThat(result.toString()).isEqualTo(expected); } - @Test - public void queryBooksWithWhereVariableCriteriaExpression() { - //given - String query = "query($where: BooksCriteriaExpression) {" + - " Books (where: $where) {" + - " select {" + - " id" + - " title" + - " genre" + - " }" + - " }" + - "}"; - - Map variables = map(entry("where", - map(entry("title", - map(entry("LIKE", - "War")))))); - String expected = "{Books={select=[" - + "{id=2, title=War and Peace, genre=NOVEL}" - + "]}}"; - - //when - Object result = executor.execute(query, variables).getData(); - - // then - assertThat(result.toString()).isEqualTo(expected); - } - - @Test - public void queryBooksWithWhereVariableCriteriaEnumListExpression() { - //given - String query = "query($where: BooksCriteriaExpression) {" + - " Books (where: $where) {" + - " select {" + - " id" + - " title" + - " genre" + - " }" + - " }" + - "}"; - - Map variables = map(entry("where", - map(entry("genre", - map(entry("IN", - Arrays.asList("NOVEL"))))))); - - String expected = "{Books={select=[" - + "{id=2, title=War and Peace, genre=NOVEL}, " - + "{id=3, title=Anna Karenina, genre=NOVEL}" - + "]}}"; - - //when - Object result = executor.execute(query, variables).getData(); - - // then - assertThat(result.toString()).isEqualTo(expected); - } - - @Test - public void queryBooksWithWhereVariableCriteriaEnumExpression() { - //given - String query = "query($where: BooksCriteriaExpression) {" + - " Books (where: $where) {" + - " select {" + - " id" + - " title" + - " genre" + - " }" + - " }" + - "}"; - - Map variables = map(entry("where", - map(entry("genre", - map(entry("IN", - "NOVEL")))))); - String expected = "{Books={select=[" - + "{id=2, title=War and Peace, genre=NOVEL}, " - + "{id=3, title=Anna Karenina, genre=NOVEL}" - + "]}}"; - - //when - Object result = executor.execute(query, variables).getData(); - - // then - assertThat(result.toString()).isEqualTo(expected); - } - - - @SafeVarargs - static Map map(Entry... entries) { - if (entries.length == 0) { // implicit null check of entries array - return Collections.emptyMap(); - } else if (entries.length == 1) { - return Collections.singletonMap(entries[0].getKey(), entries[0].getValue()); - } else { - return Stream.of(entries) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - } - - static Entry entry(K k, V v) { - return new AbstractMap.SimpleImmutableEntry<>(k, v); - } - } \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java index d2a286fb3..b5ee97df3 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java @@ -1,10 +1,10 @@ package com.introproventures.graphql.jpa.query.schema; -import static org.assertj.core.api.Assertions.tuple; -import static org.assertj.core.api.BDDAssertions.then; - import static com.introproventures.graphql.jpa.query.schema.model.book.Genre.NOVEL; import static com.introproventures.graphql.jpa.query.schema.model.book.Genre.PLAY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.assertj.core.api.BDDAssertions.then; import java.io.IOException; import java.util.Map; @@ -287,7 +287,106 @@ public void queryWithRestrictionsForMultipleProperties() throws IOException { .extracting("id", "title") .containsOnly(tuple(5L, "The Cherry Orchard")); } - + + @Test + public void queryBooksWithWhereVariableCriteriaEnumListExpression() throws IOException { + //given + String query = "query($where: BooksCriteriaExpression) {" + + " Books (where: $where) {" + + " select {" + + " id" + + " title" + + " genre" + + " }" + + " }" + + "}"; + + String variables = "{" + + " \"where\": {" + + " \"genre\": {" + + " \"IN\": [\"NOVEL\"]" + + " }" + + " } " + + "}"; + + String expected = "{Books={select=[" + + "{id=2, title=War and Peace, genre=NOVEL}, " + + "{id=3, title=Anna Karenina, genre=NOVEL}" + + "]}}"; + + //when + Object result = executor.execute(query, getVariablesMap(variables)).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryBooksWithWhereVariableCriteriaEnumExpression() throws IOException { + //given + String query = "query($where: BooksCriteriaExpression) {" + + " Books (where: $where) {" + + " select {" + + " id" + + " title" + + " genre" + + " }" + + " }" + + "}"; + + String variables = "{" + + " \"where\": {" + + " \"genre\": {" + + " \"IN\": \"NOVEL\"" + + " }" + + " } " + + "}"; + + String expected = "{Books={select=[" + + "{id=2, title=War and Peace, genre=NOVEL}, " + + "{id=3, title=Anna Karenina, genre=NOVEL}" + + "]}}"; + + //when + Object result = executor.execute(query, getVariablesMap(variables)).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryBooksWithWhereVariableCriteriaEQEnumExpression() throws IOException { + //given + String query = "query($where: BooksCriteriaExpression) {" + + " Books (where: $where) {" + + " select {" + + " id" + + " title" + + " genre" + + " }" + + " }" + + "}"; + + String variables = "{" + + " \"where\": {" + + " \"genre\": {" + + " \"EQ\": \"NOVEL\"" + + " }" + + " } " + + "}"; + + String expected = "{Books={select=[" + + "{id=2, title=War and Peace, genre=NOVEL}, " + + "{id=3, title=Anna Karenina, genre=NOVEL}" + + "]}}"; + + //when + Object result = executor.execute(query, getVariablesMap(variables)).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + @SuppressWarnings("unchecked") private Map getVariablesMap(String variables) throws IOException { ObjectMapper mapper = new ObjectMapper(); From 1c4371f61fb8074cd4b4b03d0253d59d600a8347 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Tue, 20 Aug 2019 00:46:50 -0700 Subject: [PATCH 4/4] fix: polish conversion of ArrayValue --- .../impl/QraphQLJpaBaseDataFetcher.java | 69 +++++++++---------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java index 6e9ab5bfe..ee92c789b 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java @@ -917,44 +917,41 @@ else if (value instanceof VariableReference) { return argumentValue; } } else if (value instanceof ArrayValue) { - Object convertedValue = environment.getArgument(argument.getName()); - - if (convertedValue != null && getJavaType(environment, argument).isEnum()) { - - Function f = (obj) -> Value.class.isInstance(obj) - ? Value.class.cast(obj) - : new EnumValue(obj.toString()); - - // unwrap [[EnumValue{name='value'}]] - if(convertedValue instanceof Collection - && ((Collection) convertedValue).stream().allMatch(it->it instanceof Collection)) { - convertedValue = ((Collection) convertedValue).iterator().next(); + Collection arrayValue = environment.getArgument(argument.getName()); + + if (arrayValue != null) { + // Let's unwrap array of array values + if(arrayValue.stream() + .allMatch(it->it instanceof Collection)) { + arrayValue = Collection.class.cast(arrayValue.iterator() + .next()); } - - if(convertedValue instanceof Collection) { - return ((Collection) convertedValue).stream() - .map((it) -> convertValue(environment, argument, f.apply(it))) - .collect(Collectors.toList()); - } - // Return real typed resolved array value - return convertValue(environment, argument, f.apply(convertedValue)); - } - else - if (convertedValue != null && !getJavaType(environment, argument).isEnum()) { - // unwrap [[EnumValue{name='value'}]] - if(convertedValue instanceof Collection - && ((Collection) convertedValue).stream().allMatch(it->it instanceof Collection)) { - convertedValue = ((Collection) convertedValue).iterator().next(); - } - - if(convertedValue instanceof Collection - && ((Collection) convertedValue).stream().anyMatch(it->it instanceof Value)) { - return ((Collection) convertedValue).stream() - .map((it) -> convertValue(environment, argument, (Value) it)) - .collect(Collectors.toList()); + + // Let's convert enum types, i.e. array of strings or EnumValue into Java type + if(getJavaType(environment, argument).isEnum()) { + Function objectValue = (obj) -> Value.class.isInstance(obj) + ? Value.class.cast(obj) + : new EnumValue(obj.toString()); + // Return real typed resolved array values converted into Java enums + return arrayValue.stream() + .map((it) -> convertValue(environment, + argument, + objectValue.apply(it))) + .collect(Collectors.toList()); + } + // Let's try handle Ast Value types + else if(arrayValue.stream() + .anyMatch(it->it instanceof Value)) { + return arrayValue.stream() + .map(it -> convertValue(environment, + argument, + Value.class.cast(it))) + .collect(Collectors.toList()); + } + // Return real typed resolved array value, i.e. Date, UUID, Long + else { + return arrayValue; } - // Return real typed resolved array value - return convertedValue; } else { // Wrap converted values in ArrayList return ((ArrayValue) value).getValues().stream()