From c0b95831d1ec6a2f01648b75045293639a78ff78 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 6 May 2019 21:10:55 -0700 Subject: [PATCH 01/12] feat: test exists criteria api --- .../converter/GraphQLJpaConverterTests.java | 42 +- .../jpa/query/converter/model/TaskEntity.java | 376 ++++++++++++++++++ .../jpa/query/converter/model/TaskStatus.java | 11 + .../converter/model/TaskVariableEntity.java | 21 + .../resources/GraphQLJpaConverterTests.sql | 7 + 5 files changed, 453 insertions(+), 4 deletions(-) create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskEntity.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskStatus.java diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java index 664ba9d0a..44f713b5e 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java @@ -26,12 +26,15 @@ import javax.persistence.Query; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import javax.persistence.criteria.Subquery; import javax.transaction.Transactional; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.introproventures.graphql.jpa.query.converter.model.JsonEntity; +import com.introproventures.graphql.jpa.query.converter.model.TaskEntity; import com.introproventures.graphql.jpa.query.converter.model.TaskVariableEntity; import com.introproventures.graphql.jpa.query.converter.model.VariableValue; import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; @@ -119,16 +122,17 @@ public void criteriaTester() { @Test @Transactional public void criteriaTester2() { - CriteriaBuilder builder = entityManager.getCriteriaBuilder(); - CriteriaQuery criteria = builder.createQuery(TaskVariableEntity.class); + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery criteria = cb.createQuery(TaskVariableEntity.class); Root taskVariable = criteria.from(TaskVariableEntity.class); Boolean object = new Boolean(true); VariableValue variableValue = new VariableValue<>(object); criteria.select(taskVariable) - .where(builder.equal(taskVariable.get("value"), variableValue)); - + .where(cb.and(cb.equal(cb.lower(taskVariable.get("name")), "variable2")), + cb.in(taskVariable.get("value")).value(variableValue)); + // when: List result = entityManager.createQuery(criteria).getResultList(); @@ -136,6 +140,36 @@ public void criteriaTester2() { assertThat(result).isNotEmpty(); assertThat(result).hasSize(1); } + + @Test + @Transactional + public void criteriaTester3() { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + + CriteriaQuery tasksQuery = cb.createQuery(TaskEntity.class); + Root task = tasksQuery.from(TaskEntity.class); + + Subquery subquery = tasksQuery.subquery(TaskVariableEntity.class); + Root taskVariables = subquery.from(TaskVariableEntity.class); + + Predicate isOwner = cb.in(taskVariables.get("task")).value(task); + + Predicate var1 = cb.and(cb.equal(taskVariables.get("name"), "variable2"), + cb.equal(taskVariables.get("value"), new VariableValue<>(new Boolean(true)))); + + Predicate var2 = cb.and(cb.equal(taskVariables.get("name"), "variable1"), + cb.equal(taskVariables.get("value"), new VariableValue<>(new String("data")))); + + tasksQuery.select(task) + .where(cb.and(cb.exists(subquery.select(taskVariables).where(cb.and(isOwner, var1))), + cb.exists(subquery.select(taskVariables).where(cb.and(isOwner, var2))))); + // when: + List result = entityManager.createQuery(tasksQuery).getResultList(); + + // then: + assertThat(result).isNotEmpty(); + assertThat(result).hasSize(1); + } @Test // Problem with generating cast() in the where expression @Transactional diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskEntity.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskEntity.java new file mode 100644 index 000000000..a02351ad9 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskEntity.java @@ -0,0 +1,376 @@ +package com.introproventures.graphql.jpa.query.converter.model; + +import java.util.Date; +import java.util.Set; + +import javax.persistence.ConstraintMode; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Transient; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.format.annotation.DateTimeFormat; + +@Entity(name="Task") +@Table(name = "TASK", + indexes= { + @Index(name="task_status_idx", columnList="status", unique=false), + @Index(name="task_processInstance_idx", columnList="processInstanceId", unique=false) +}) +public class TaskEntity extends ActivitiEntityMetadata { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 1L; + + @Id + private String id; + private String assignee; + private String name; + private String description; + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date createdDate; + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date dueDate; + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date claimedDate; + private int priority; + private String processDefinitionId; + private String processInstanceId; + @Enumerated(EnumType.STRING) + private TaskStatus status; + private String owner; + private String parentTaskId; + private String formKey; + private Date completedDate; + private Long duration; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date lastModified; + + @JsonIgnore + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date createdTo; + + @JsonIgnore + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date createdFrom; + + @JsonIgnore + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date lastModifiedTo; + + @JsonIgnore + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date lastModifiedFrom; + + @JsonIgnore + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date lastClaimedTo; + + @JsonIgnore + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date lastClaimedFrom; + + @JsonIgnore + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date completedTo; + + @JsonIgnore + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date completedFrom; + + @JsonIgnore + @OneToMany(fetch = FetchType.LAZY) + @JoinColumn(name = "taskId", referencedColumnName = "id", insertable = false, updatable = false + , foreignKey = @javax.persistence.ForeignKey(value = ConstraintMode.NO_CONSTRAINT, name = "none")) + + private Set variables; + + public TaskEntity() { + } + + public TaskEntity(String id, + String assignee, + String name, + String description, + Date createTime, + Date dueDate, + int priority, + String processDefinitionId, + String processInstanceId, + String serviceName, + String serviceFullName, + String serviceVersion, + String appName, + String appVersion, + TaskStatus status, + Date lastModified, + Date claimedDate, + String owner, + String parentTaskId, + String formKey) { + super(serviceName, + serviceFullName, + serviceVersion, + appName, + appVersion); + this.id = id; + this.assignee = assignee; + this.name = name; + this.description = description; + this.createdDate = createTime; + this.dueDate = dueDate; + this.priority = priority; + this.processDefinitionId = processDefinitionId; + this.processInstanceId = processInstanceId; + this.status = status; + this.lastModified = lastModified; + this.claimedDate = claimedDate; + this.owner = owner; + this.parentTaskId = parentTaskId; + this.formKey = formKey; + } + + public String getId() { + return id; + } + + public String getAssignee() { + return assignee; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Date getCreatedDate() { + return createdDate; + } + + public Date getDueDate() { + return dueDate; + } + + public int getPriority() { + return priority; + } + + public String getProcessDefinitionId() { + return processDefinitionId; + } + + public String getProcessInstanceId() { + return processInstanceId; + } + + public boolean isStandAlone() { + return processInstanceId == null; + } + + public TaskStatus getStatus() { + return status; + } + + public Date getLastModified() { + return lastModified; + } + + public void setId(String id) { + this.id = id; + } + + public void setAssignee(String assignee) { + this.assignee = assignee; + } + + public void setName(String name) { + this.name = name; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setCreatedDate(Date createdDate) { + this.createdDate = createdDate; + } + + public void setDueDate(Date dueDate) { + this.dueDate = dueDate; + } + + public void setPriority(int priority) { + this.priority = priority; + } + + public void setProcessDefinitionId(String processDefinitionId) { + this.processDefinitionId = processDefinitionId; + } + + public void setProcessInstanceId(String processInstanceId) { + this.processInstanceId = processInstanceId; + } + + public void setStatus(TaskStatus status) { + this.status = status; + } + + public void setLastModified(Date lastModified) { + this.lastModified = lastModified; + } + + @Transient + public Date getLastModifiedTo() { + return lastModifiedTo; + } + + public void setLastModifiedTo(Date lastModifiedTo) { + this.lastModifiedTo = lastModifiedTo; + } + + @Transient + public Date getLastModifiedFrom() { + return lastModifiedFrom; + } + + public Date getClaimedDate() { + return claimedDate; + } + + public void setClaimedDate(Date claimedDate) { + this.claimedDate = claimedDate; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public void setLastModifiedFrom(Date lastModifiedFrom) { + this.lastModifiedFrom = lastModifiedFrom; + } + + /** + * @return the variableEntities + */ + public Set getVariables() { + return this.variables; + } + + /** + * @param variables the variableEntities to set + */ + public void setVariables(Set variables) { + this.variables = variables; + } + + + public String getParentTaskId() { + return parentTaskId; + } + + public void setParentTaskId(String parentTaskId) { + this.parentTaskId = parentTaskId; + } + + public String getFormKey() { + return formKey; + } + + public void setFormKey(String formKey) { + this.formKey = formKey; + } + + public Long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } + + @Transient + public Date getCompletedDate() { + return completedDate; + } + + public void setCompletedDate(Date endDate) { + this.completedDate = endDate; + } + + @Transient + public Date getCreatedTo() { + return createdTo; + } + + + public void setCreatedTo(Date createdTo) { + this.createdTo = createdTo; + } + + @Transient + public Date getCreatedFrom() { + return createdFrom; + } + + + public void setCreatedFrom(Date createdFrom) { + this.createdFrom = createdFrom; + } + + @Transient + public Date getLastClaimedTo() { + return lastClaimedTo; + } + + + public void setLastClaimedTo(Date lastClaimedTo) { + this.lastClaimedTo = lastClaimedTo; + } + + @Transient + public Date getLastClaimedFrom() { + return lastClaimedFrom; + } + + + public void setLastClaimedFrom(Date lastClaimedFrom) { + this.lastClaimedFrom = lastClaimedFrom; + } + + @Transient + public Date getCompletedTo() { + return completedTo; + } + + + public void setCompletedTo(Date completedTo) { + this.completedTo = completedTo; + } + + @Transient + public Date getCompletedFrom() { + return completedFrom; + } + + public void setCompletedFrom(Date completedFrom) { + this.completedFrom = completedFrom; + } + +} diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskStatus.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskStatus.java new file mode 100644 index 000000000..aa7fab3c6 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskStatus.java @@ -0,0 +1,11 @@ +package com.introproventures.graphql.jpa.query.converter.model; + + +enum TaskStatus { + CREATED, + ASSIGNED, + SUSPENDED, + COMPLETED, + CANCELLED, + DELETED +} \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java index 9ea3365b0..8033d905b 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java @@ -2,15 +2,27 @@ import java.util.Date; +import javax.persistence.ConstraintMode; import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import javax.persistence.Table; +import com.fasterxml.jackson.annotation.JsonIgnore; + @Entity(name="TaskVariable") @Table(name = "TASK_VARIABLE") public class TaskVariableEntity extends AbstractVariableEntity { private String taskId; + @JsonIgnore + @ManyToOne(optional = true, fetch=FetchType.LAZY) + @JoinColumn(name = "taskId", referencedColumnName = "id", insertable = false, updatable = false, nullable = true + , foreignKey = @javax.persistence.ForeignKey(value = ConstraintMode.NO_CONSTRAINT, name = "none")) + private TaskEntity task; + public TaskVariableEntity() { } @@ -55,4 +67,13 @@ public boolean isTaskVariable() { return true; } + public TaskEntity getTask() { + return this.task; + } + + public void setTask(TaskEntity taskEntity) { + this.task = taskEntity; + } + + } \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql b/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql index d14d6c0e1..688c917fb 100644 --- a/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql +++ b/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql @@ -3,6 +3,13 @@ insert into json_entity (id, first_name, last_name, attributes) values (1, 'john', 'doe', '{"attr":{"key":["1","2","3","4","5"]}}'), (2, 'joe', 'smith', '{"attr":["1","2","3","4","5"]}'); +insert into TASK (id, assignee, created_date, description, due_date, last_modified, last_modified_from, last_modified_to, name, priority, process_definition_id, process_instance_id, status, owner, claimed_date) values + ('1', 'assignee', CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task1', 5, 'process_definition_id', 0, 'COMPLETED' , 'owner', null), + ('2', 'assignee', CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task2', 10, 'process_definition_id', 0, 'CREATED' , 'owner', null), + ('3', 'assignee', CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task3', 5, 'process_definition_id', 0, 'CREATED' , 'owner', null), + ('4', 'assignee', CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task4', 10, 'process_definition_id', 1, 'CREATED' , 'owner', null), + ('5', 'assignee', CURRENT_TIMESTAMP, 'description', null, null, null, null, 'task5', 5, 'process_definition_id', 1, 'COMPLETED' , 'owner', null); + insert into PROCESS_VARIABLE (create_time, execution_id, last_updated_time, name, process_instance_id, type, value) values (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'document', 1, 'json', '{"value":{"key":["1","2","3","4","5"]}}'); From 15771a092bd72f783346217c6cd40dde12e319c3 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Sun, 9 Jun 2019 19:14:31 -0700 Subject: [PATCH 02/12] feat: add correlation subquery testers --- .../converter/GraphQLJpaConverterTests.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java index 44f713b5e..1a1dec48b 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java @@ -26,6 +26,7 @@ import javax.persistence.Query; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.Subquery; @@ -171,6 +172,66 @@ public void criteriaTester3() { assertThat(result).hasSize(1); } + @Test + @Transactional + public void criteriaTester4() { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + + CriteriaQuery tasksQuery = cb.createQuery(TaskEntity.class); + Root task = tasksQuery.from(TaskEntity.class); + + Subquery subquery = tasksQuery.subquery(TaskVariableEntity.class); + Root taskCorrelation = subquery.correlate(task); + + Join taskVariables = taskCorrelation.join("variables"); + + Predicate var1 = cb.and(cb.equal(taskVariables.get("name"), "variable2"), + cb.equal(taskVariables.get("value"), new VariableValue<>(new Boolean(true)))); + + Predicate var2 = cb.and(cb.equal(taskVariables.get("name"), "variable1"), + cb.equal(taskVariables.get("value"), new VariableValue<>(new String("data")))); + + tasksQuery.select(task) + .where(cb.and(cb.exists(subquery.select(taskVariables).where(var1)), + cb.exists(subquery.select(taskVariables).where(var2)))); + // when: + List result = entityManager.createQuery(tasksQuery).getResultList(); + + // then: + assertThat(result).isNotEmpty(); + assertThat(result).hasSize(1); + } + + @Test + @Transactional + public void criteriaTester5() { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + + CriteriaQuery tasksQuery = cb.createQuery(TaskEntity.class); + Root task = tasksQuery.from(TaskEntity.class); + + Subquery subquery = tasksQuery.subquery(TaskVariableEntity.class); + Root taskCorrelation = subquery.correlate(task); + + Join taskVariables = taskCorrelation.join("variables"); + + Predicate var1 = cb.and(cb.equal(taskVariables.get("name"), "variable2"), + cb.equal(taskVariables.get("value"), new VariableValue<>(new Boolean(true)))); + + Predicate var2 = cb.and(cb.equal(taskVariables.get("name"), "variable1"), + cb.equal(taskVariables.get("value"), new VariableValue<>(new String("data")))); + + tasksQuery.select(task) + .where(cb.not(cb.and(cb.exists(subquery.select(taskVariables).where(var1)), + cb.exists(subquery.select(taskVariables).where(var2))))); + // when: + List result = entityManager.createQuery(tasksQuery).getResultList(); + + // then: + assertThat(result).isNotEmpty(); + assertThat(result).hasSize(4); + } + @Test // Problem with generating cast() in the where expression @Transactional public void criteriaTesterLike() { From f2afa631fe8d848b77d20aec45b98689043e3bdb Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 10 Jun 2019 17:40:06 -0700 Subject: [PATCH 03/12] feat: add singular EXISTS logical subquery expression --- .../schema/impl/GraphQLJpaSchemaBuilder.java | 12 ++ .../jpa/query/schema/impl/Logical.java | 2 +- .../impl/QraphQLJpaBaseDataFetcher.java | 54 +++++-- .../converter/GraphQLJpaConverterTests.java | 38 ++++- .../query/schema/GraphQLExecutorTests.java | 145 +++++++++++++++++- 5 files changed, 238 insertions(+), 13 deletions(-) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java index da8008798..2a968f544 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java @@ -253,6 +253,12 @@ private GraphQLArgument computeWhereArgument(ManagedType managedType) { .type(new GraphQLList(new GraphQLTypeReference(type))) .build() ) + .field(GraphQLInputObjectField.newInputObjectField() + .name("EXISTS") + .description("EXISTS Subquery expression") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() + ) .fields(managedType.getAttributes().stream() .filter(this::isValidInput) .filter(this::isNotIgnored) @@ -324,6 +330,12 @@ private GraphQLInputObjectType computeWhereInputType(ManagedType managedType) .type(new GraphQLList(new GraphQLTypeReference(type))) .build() ) + .field(GraphQLInputObjectField.newInputObjectField() + .name("EXISTS") + .description("EXISTS Subquery expression") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() + ) .fields(managedType.getAttributes().stream() .filter(this::isValidInput) .filter(this::isNotIgnored) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/Logical.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/Logical.java index d95eeb40f..768bc7137 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/Logical.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/Logical.java @@ -21,7 +21,7 @@ import java.util.stream.Collectors; enum Logical { - AND, OR; + AND, OR, EXISTS; private static Set names = EnumSet.allOf(Logical.class) .stream() 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 39d790dce..659224f08 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 @@ -38,6 +38,7 @@ import javax.persistence.EntityManager; import javax.persistence.Subgraph; import javax.persistence.TypedQuery; +import javax.persistence.criteria.AbstractQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Fetch; @@ -47,6 +48,7 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import javax.persistence.criteria.Subquery; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.Attribute.PersistentAttributeType; import javax.persistence.metamodel.EntityType; @@ -131,10 +133,13 @@ protected TypedQuery getQuery(DataFetchingEnvironment environment, Field fiel CriteriaQuery query = cb.createQuery((Class) entityType.getJavaType()); Root from = query.from(entityType); - from.alias(from.getModel().getName()); + DataFetchingEnvironment queryEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment) + .root(query) + .build(); + from.alias(from.getModel().getName().toLowerCase()); // Build predicates from query arguments - List predicates = getFieldPredicates(field, query, cb,from, from, environment); + List predicates = getFieldPredicates(field, query, cb,from, from, queryEnvironment); // Use AND clause to filter results if(!predicates.isEmpty()) @@ -223,14 +228,23 @@ protected final List getFieldPredicates(Field field, CriteriaQuery // Let's build join fetch graph to avoid Hibernate error: // "query specified join fetching, but the owner of the fetched association was not present in the select list" if(selectedField.getSelectionSet() != null && fetch != null) { + Map variables = environment.getExecutionContext().getVariables(); + GraphQLFieldDefinition fieldDefinition = getFieldDef(environment.getGraphQLSchema(), this.getObjectType(environment), selectedField); - Map args = environment.getArguments(); + + List values = whereArgument.map(Collections::singletonList) + .orElse(Collections.emptyList()); + + Map fieldArguments = new ValuesResolver().getArgumentValues(fieldDefinition.getArguments(), + values, + variables); DataFetchingEnvironment fieldEnvironment = wherePredicateEnvironment(environment, fieldDefinition, - args); + fieldArguments); + predicates.addAll(getFieldPredicates(selectedField, query, cb, root, fetch, fieldEnvironment)); } } @@ -360,6 +374,8 @@ protected Predicate getPredicate(CriteriaBuilder cb, Root from, From pat } } } + + @SuppressWarnings( "unchecked" ) private R getValue(Argument argument) { @@ -427,11 +443,31 @@ protected Predicate getArgumentPredicate(CriteriaBuilder cb, From from, GraphQLFieldDefinition fieldDefinition = getFieldDef(environment.getGraphQLSchema(), this.getObjectType(environment), new Field(it.getName())); - boolean isOptional = false; - - return getArgumentPredicate(cb, reuseJoin(from, it.getName(), isOptional), - wherePredicateEnvironment(environment, fieldDefinition, args), - arg); + if(Logical.EXISTS == logical) { + AbstractQuery query = environment.getRoot(); + Subquery subquery = query.subquery(attribute.getJavaType()); + From correlation = Root.class.isInstance(from) + ? subquery.correlate((Root) from) + : subquery.correlate((Join) from); + + Join correlationJoin = correlation.join(it.getName()); + + DataFetchingEnvironment existsEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment) + .root(subquery) + .build(); + + Predicate restriction = getArgumentPredicate(cb, + correlationJoin, + wherePredicateEnvironment(existsEnvironment, fieldDefinition, args), + arg); + + return cb.exists(subquery.select((Join) correlationJoin).where(restriction)); + + } else { + return getArgumentPredicate(cb, reuseJoin(from, it.getName(), false), + wherePredicateEnvironment(environment, fieldDefinition, args), + arg); + } } } diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java index 1a1dec48b..2cf4cb777 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java @@ -222,8 +222,10 @@ public void criteriaTester5() { cb.equal(taskVariables.get("value"), new VariableValue<>(new String("data")))); tasksQuery.select(task) - .where(cb.not(cb.and(cb.exists(subquery.select(taskVariables).where(var1)), - cb.exists(subquery.select(taskVariables).where(var2))))); + .where(cb.and(cb.not(cb.exists(subquery.select(taskVariables).where(var1))), + cb.not(cb.exists(subquery.select(taskVariables).where(var2))) + ) + ); // when: List result = entityManager.createQuery(tasksQuery).getResultList(); @@ -232,6 +234,38 @@ public void criteriaTester5() { assertThat(result).hasSize(4); } + @Test + @Transactional + public void criteriaTester6() { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + + CriteriaQuery tasksQuery = cb.createQuery(TaskEntity.class); + Root task = tasksQuery.from(TaskEntity.class); + + Subquery subquery = tasksQuery.subquery(TaskVariableEntity.class); + Root taskCorrelation = subquery.correlate(task); + + Join taskVariables = taskCorrelation.join("variables"); + + Predicate var1 = cb.and(cb.equal(taskVariables.get("name"), "variable2"), + cb.equal(taskVariables.get("value"), new VariableValue<>(new Boolean(true)))); + + Predicate var2 = cb.and(cb.equal(taskVariables.get("name"), "variable1"), + cb.equal(taskVariables.get("value"), new VariableValue<>(new String("data")))); + + tasksQuery.select(task) + .where(cb.not(cb.or(cb.exists(subquery.select(taskVariables).where(var1)), + cb.exists(subquery.select(taskVariables).where(var2))) + ) + ); + // when: + List result = entityManager.createQuery(tasksQuery).getResultList(); + + // then: + assertThat(result).isNotEmpty(); + assertThat(result).hasSize(4); + } + @Test // Problem with generating cast() in the where expression @Transactional public void criteriaTesterLike() { 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 94ddb8c5f..9ea599292 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 @@ -608,8 +608,151 @@ public void queryForBooksWithWhereAuthorEqIdWithVariables() { // then assertThat(result.toString()).isEqualTo(expected); } - + + @Test + public void queryForAuthorsWithWhereEXISTSBooksLIKETitle() { + //given + String query = "query { " + + "Authors(where: {" + + " EXISTS: {" + + " books: {" + + " title: {LIKE: \"War\"}" + + " }" + + " }" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " }"+ + "}"; + + String expected = "{Authors={select=[" + + "{id=1, name=Leo Tolstoy, books=[" + + "{id=2, title=War and Peace}, " + + "{id=3, title=Anna Karenina}" + + "]}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryForAuthorsWithWhereEXISTSBooksLIKETitleANDAuthorLIKEName() { + //given + String query = "query { " + + "Authors(where: {" + + " EXISTS: {" + + " books: {" + + " author: {name: {LIKE: \"Leo\"}}" + + " title: {LIKE: \"War\"}" + + " }" + + " }" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " }"+ + "}"; + + String expected = "{Authors={select=[" + + "{id=1, name=Leo Tolstoy, books=[" + + "{id=2, title=War and Peace}, " + + "{id=3, title=Anna Karenina}" + + "]}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryForAuthorsWithWhereEXISTSBooksLIKETitleANDEXISTSAuthorLIKEName() { + //given + String query = "query { " + + " Authors(where: {" + + " EXISTS: {" + + " books: {" + + " EXISTS: {" + + " author: {name: {LIKE: \"Leo\"}} " + + " }" + + " title: {LIKE: \"War\"}" + + " }" + + " }" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " }"+ + "}"; + + String expected = "{Authors={select=[" + + "{id=1, name=Leo Tolstoy, books=[" + + "{id=2, title=War and Peace}, " + + "{id=3, title=Anna Karenina}" + + "]}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryForAuthorsWithWhereEXISTSBooksLIKETitleEmpty() { + //given + String query = "query { " + + "Authors(where: {" + + " EXISTS: {" + + " books: {" + + " author: {name: {LIKE: \"Anton\"}}" + + " title: {LIKE: \"War\"}" + + " }" + + " }" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " }"+ + "}"; + + String expected = "{Authors={select=[]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } @Test public void queryForAuthorssWithWhereBooksGenreEquals() { From 1a07d57edd83fe2bf5d5a237fd79010fc73ce593 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 10 Jun 2019 18:27:21 -0700 Subject: [PATCH 04/12] feat: add list EXISTS criteria expressions --- .../impl/QraphQLJpaBaseDataFetcher.java | 20 +++-- .../GraphQLExecutorListCriteriaTests.java | 87 +++++++++++++++++++ 2 files changed, 99 insertions(+), 8 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 659224f08..5d1d59aa1 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 @@ -401,6 +401,7 @@ protected Predicate getWherePredicate(CriteriaBuilder cb, Root root, From from, DataFetchingEnvironment environment, Argument argument) { ObjectValue whereValue = getValue(argument); @@ -443,12 +444,15 @@ protected Predicate getArgumentPredicate(CriteriaBuilder cb, From from, GraphQLFieldDefinition fieldDefinition = getFieldDef(environment.getGraphQLSchema(), this.getObjectType(environment), new Field(it.getName())); + + Boolean isOptional = false; + if(Logical.EXISTS == logical) { AbstractQuery query = environment.getRoot(); Subquery subquery = query.subquery(attribute.getJavaType()); From correlation = Root.class.isInstance(from) - ? subquery.correlate((Root) from) - : subquery.correlate((Join) from); + ? subquery.correlate((Root) from) + : subquery.correlate((Join) from); Join correlationJoin = correlation.join(it.getName()); @@ -462,12 +466,11 @@ protected Predicate getArgumentPredicate(CriteriaBuilder cb, From from, arg); return cb.exists(subquery.select((Join) correlationJoin).where(restriction)); - - } else { - return getArgumentPredicate(cb, reuseJoin(from, it.getName(), false), - wherePredicateEnvironment(environment, fieldDefinition, args), - arg); } + + return getArgumentPredicate(cb, reuseJoin(from, it.getName(), isOptional), + wherePredicateEnvironment(environment, fieldDefinition, args), + arg); } } @@ -545,11 +548,12 @@ protected Predicate getArgumentsPredicate(CriteriaBuilder cb, Attribute attribute = getAttribute(environment, arg); if(attribute.isAssociation()) { + GraphQLFieldDefinition fieldDefinition = getFieldDef(environment.getGraphQLSchema(), this.getObjectType(environment), new Field(it.getName())); boolean isOptional = false; - + return getArgumentPredicate(cb, reuseJoin(path, it.getName(), isOptional), wherePredicateEnvironment(environment, fieldDefinition, args), arg); diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorListCriteriaTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorListCriteriaTests.java index a31bc88cc..123ca6ca0 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorListCriteriaTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorListCriteriaTests.java @@ -407,4 +407,91 @@ public void queryWithWhereANDNestedOneToManyCompoundORListCriteria() { assertThat(result.toString()).isEqualTo(expected); } + @Test + public void queryWithWhereANDListEXISTSMixedCriteria() { + //given: + String query = "query { " + + " Authors(where: {" + + " books: {" + + " AND: [{" + + " EXISTS: {" + + " author: {" + + " name: {LIKE: \"Leo\"}" + + " }" + + " }" + + " }, {" + + " title: {LIKE: \"War\"}" + + " }]" + + " }" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " } " + + "}"; + + String expected = "{Authors={select=[" + + "{id=1, name=Leo Tolstoy, books=[" + + "{id=2, title=War and Peace}, " + + "{id=3, title=Anna Karenina}]}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryWithWhereANDListEXISTSCriteria() { + //given: + String query = "query { " + + " Authors(where: {" + + " AND: [{ " + + " EXISTS: {" + + " books: {" + + " title: {LIKE: \"War\"}" + + " id: {EQ: 2}" + + " }" + + " }" + + " }, { " + + " EXISTS: {" + + " books: {" + + " title: {LIKE: \"Anna\"}" + + " id: {EQ: 3}" + + " }" + + " }" + + " }]" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " }" + + "}"; + + String expected = "{Authors={select=[" + + "{id=1, name=Leo Tolstoy, books=[" + + "{id=2, title=War and Peace}, " + + "{id=3, title=Anna Karenina}]}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + } \ No newline at end of file From fea3134e7ecd0d9fe81e20d4cc5322a2658020e0 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 10 Jun 2019 18:49:45 -0700 Subject: [PATCH 05/12] feat: add NOT_EXISTS subquery criteria expression --- .../schema/impl/GraphQLJpaSchemaBuilder.java | 28 +++++-- .../jpa/query/schema/impl/Logical.java | 2 +- .../impl/QraphQLJpaBaseDataFetcher.java | 13 ++-- .../query/schema/GraphQLExecutorTests.java | 75 +++++++++++++++++++ 4 files changed, 104 insertions(+), 14 deletions(-) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java index 2a968f544..6590a698f 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java @@ -254,10 +254,16 @@ private GraphQLArgument computeWhereArgument(ManagedType managedType) { .build() ) .field(GraphQLInputObjectField.newInputObjectField() - .name("EXISTS") - .description("EXISTS Subquery expression") - .type(new GraphQLList(new GraphQLTypeReference(type))) - .build() + .name("EXISTS") + .description("Logical EXISTS subquery expression") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() + ) + .field(GraphQLInputObjectField.newInputObjectField() + .name("NOT_EXISTS") + .description("Logical NOT EXISTS subquery expression") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() ) .fields(managedType.getAttributes().stream() .filter(this::isValidInput) @@ -331,10 +337,16 @@ private GraphQLInputObjectType computeWhereInputType(ManagedType managedType) .build() ) .field(GraphQLInputObjectField.newInputObjectField() - .name("EXISTS") - .description("EXISTS Subquery expression") - .type(new GraphQLList(new GraphQLTypeReference(type))) - .build() + .name("EXISTS") + .description("Logical EXISTS subquery expression") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() + ) + .field(GraphQLInputObjectField.newInputObjectField() + .name("NOT_EXISTS") + .description("Logical NOT EXISTS subquery expression") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() ) .fields(managedType.getAttributes().stream() .filter(this::isValidInput) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/Logical.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/Logical.java index 768bc7137..1c5069404 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/Logical.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/Logical.java @@ -21,7 +21,7 @@ import java.util.stream.Collectors; enum Logical { - AND, OR, EXISTS; + AND, OR, EXISTS, NOT_EXISTS; private static Set names = EnumSet.allOf(Logical.class) .stream() 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 5d1d59aa1..3a8dd888e 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 @@ -447,12 +447,11 @@ protected Predicate getArgumentPredicate(CriteriaBuilder cb, From from, Boolean isOptional = false; - if(Logical.EXISTS == logical) { + if(Arrays.asList(Logical.EXISTS, Logical.NOT_EXISTS).contains(logical) ) { AbstractQuery query = environment.getRoot(); Subquery subquery = query.subquery(attribute.getJavaType()); - From correlation = Root.class.isInstance(from) - ? subquery.correlate((Root) from) - : subquery.correlate((Join) from); + From correlation = Root.class.isInstance(from) ? subquery.correlate((Root) from) + : subquery.correlate((Join) from); Join correlationJoin = correlation.join(it.getName()); @@ -465,7 +464,11 @@ protected Predicate getArgumentPredicate(CriteriaBuilder cb, From from, wherePredicateEnvironment(existsEnvironment, fieldDefinition, args), arg); - return cb.exists(subquery.select((Join) correlationJoin).where(restriction)); + + Predicate exists = cb.exists(subquery.select((Join) correlationJoin) + .where(restriction)); + + return logical == Logical.EXISTS ? exists : cb.not(exists); } return getArgumentPredicate(cb, reuseJoin(from, it.getName(), isOptional), 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 9ea599292..2d2b06907 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 @@ -750,9 +750,84 @@ public void queryForAuthorsWithWhereEXISTSBooksLIKETitleEmpty() { //when Object result = executor.execute(query).getData(); + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryForAuthorsWithWhereNOTEXISTSBooksLIKETitleWar() { + //given + String query = "query { " + + "Authors(where: {" + + " NOT_EXISTS: {" + + " books: {" + + " title: {LIKE: \"War\"}" + + " }" + + " }" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }"+ + " }"+ + "}"; + + String expected = "{Authors={select=[" + + "{id=4, name=Anton Chekhov, books=[" + + "{id=5, title=The Cherry Orchard}, " + + "{id=6, title=The Seagull}, " + + "{id=7, title=Three Sisters}]}, " + + "{id=8, name=Igor Dianov, books=[]}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + // then assertThat(result.toString()).isEqualTo(expected); } + + @Test + public void queryForAuthorsWithWhereBooksNOTEXISTSAuthorLIKENameLeo() { + //given + String query = "query { " + + " Authors(where: {" + + " books: {" + + " NOT_EXISTS: {" + + " author: {" + + " name: {LIKE: \"Leo\"}" + + " }" + + " }" + + " }" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " }"+ + "}"; + + String expected = "{Authors={select=[" + + "{id=4, name=Anton Chekhov, books=[" + + "{id=5, title=The Cherry Orchard}, " + + "{id=6, title=The Seagull}, " + + "{id=7, title=Three Sisters}]}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } @Test public void queryForAuthorssWithWhereBooksGenreEquals() { From e91ba71109acfbe994d0346e37901bcf3c007428 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 10 Jun 2019 20:32:15 -0700 Subject: [PATCH 06/12] feat: add SubqueryCriteriaExpression GraphQLInputObjectType --- .../schema/impl/GraphQLJpaSchemaBuilder.java | 115 +++++++++++++----- 1 file changed, 84 insertions(+), 31 deletions(-) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java index 6590a698f..ae145b580 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java @@ -104,6 +104,7 @@ public class GraphQLJpaSchemaBuilder implements GraphQLSchemaBuilder { private Map, GraphQLOutputType> classCache = new HashMap<>(); private Map, GraphQLObjectType> entityCache = new HashMap<>(); private Map, GraphQLInputObjectType> inputObjectCache = new HashMap<>(); + private Map, GraphQLInputObjectType> subqueryInputObjectCache = new HashMap<>(); private Map, GraphQLObjectType> embeddableOutputCache = new HashMap<>(); private Map, GraphQLInputObjectType> embeddableInputCache = new HashMap<>(); @@ -256,13 +257,13 @@ private GraphQLArgument computeWhereArgument(ManagedType managedType) { .field(GraphQLInputObjectField.newInputObjectField() .name("EXISTS") .description("Logical EXISTS subquery expression") - .type(new GraphQLList(new GraphQLTypeReference(type))) + .type(new GraphQLList(getSubqueryInputType(managedType))) .build() ) .field(GraphQLInputObjectField.newInputObjectField() .name("NOT_EXISTS") .description("Logical NOT EXISTS subquery expression") - .type(new GraphQLList(new GraphQLTypeReference(type))) + .type(new GraphQLList(getSubqueryInputType(managedType))) .build() ) .fields(managedType.getAttributes().stream() @@ -295,6 +296,58 @@ private String resolveWhereArgumentTypeName(ManagedType managedType) { return namingStrategy.pluralize(typeName)+"CriteriaExpression"; } + private String resolveSubqueryArgumentTypeName(ManagedType managedType) { + String typeName=resolveTypeName(managedType); + + return namingStrategy.pluralize(typeName)+"SubqueryCriteriaExpression"; + } + + private GraphQLInputObjectType getSubqueryInputType(ManagedType managedType) { + return subqueryInputObjectCache.computeIfAbsent(managedType, this::computeSubqueryInputType); + } + + private GraphQLInputObjectType computeSubqueryInputType(ManagedType managedType) { + String type=resolveSubqueryArgumentTypeName(managedType); + + Builder whereInputObject = GraphQLInputObjectType.newInputObject() + .name(type) + .description("Where logical AND specification of the provided list of criteria expressions") + .field(GraphQLInputObjectField.newInputObjectField() + .name(OR) + .description("Logical operation for expressions") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() + ) + .field(GraphQLInputObjectField.newInputObjectField() + .name(AND) + .description("Logical operation for expressions") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() + ) + .field(GraphQLInputObjectField.newInputObjectField() + .name("EXISTS") + .description("Logical EXISTS subquery expression") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() + ) + .field(GraphQLInputObjectField.newInputObjectField() + .name("NOT_EXISTS") + .description("Logical NOT EXISTS subquery expression") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() + ) + .fields(managedType.getAttributes().stream() + .filter(this::isValidAssociation) + .filter(this::isNotIgnored) + .filter(this::isNotIgnoredFilter) + .map(this::getWhereInputRelationField) + .collect(Collectors.toList()) + ); + + return whereInputObject.build(); + + } + private String resolveTypeName(ManagedType managedType) { String typeName=""; @@ -325,43 +378,44 @@ private GraphQLInputObjectType computeWhereInputType(ManagedType managedType) .name(type) .description("Where logical AND specification of the provided list of criteria expressions") .field(GraphQLInputObjectField.newInputObjectField() - .name(OR) - .description("Logical operation for expressions") - .type(new GraphQLList(new GraphQLTypeReference(type))) - .build() + .name(OR) + .description("Logical operation for expressions") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() ) .field(GraphQLInputObjectField.newInputObjectField() - .name(AND) - .description("Logical operation for expressions") - .type(new GraphQLList(new GraphQLTypeReference(type))) - .build() + .name(AND) + .description("Logical operation for expressions") + .type(new GraphQLList(new GraphQLTypeReference(type))) + .build() ) .field(GraphQLInputObjectField.newInputObjectField() .name("EXISTS") .description("Logical EXISTS subquery expression") - .type(new GraphQLList(new GraphQLTypeReference(type))) + .type(new GraphQLList(getSubqueryInputType(managedType))) .build() ) .field(GraphQLInputObjectField.newInputObjectField() .name("NOT_EXISTS") .description("Logical NOT EXISTS subquery expression") - .type(new GraphQLList(new GraphQLTypeReference(type))) + .type(new GraphQLList(getSubqueryInputType(managedType))) .build() ) .fields(managedType.getAttributes().stream() - .filter(this::isValidInput) - .filter(this::isNotIgnored) - .filter(this::isNotIgnoredFilter) - .map(this::getWhereInputField) - .collect(Collectors.toList()) + .filter(this::isValidInput) + .filter(this::isNotIgnored) + .filter(this::isNotIgnoredFilter) + .map(this::getWhereInputField) + .collect(Collectors.toList()) ) .fields(managedType.getAttributes().stream() - .filter(this::isValidAssociation) - .filter(this::isNotIgnored) - .filter(this::isNotIgnoredFilter) - .map(this::getWhereInputRelationField) - .collect(Collectors.toList()) + .filter(this::isValidAssociation) + .filter(this::isNotIgnored) + .filter(this::isNotIgnoredFilter) + .map(this::getWhereInputRelationField) + .collect(Collectors.toList()) ); + return whereInputObject.build(); @@ -374,23 +428,22 @@ private GraphQLInputObjectField getWhereInputRelationField(Attribute attrib String description = getSchemaDescription(attribute.getJavaMember()); return GraphQLInputObjectField.newInputObjectField() - .name(attribute.getName()) - .description(description) - .type(new GraphQLTypeReference(type)) - .build(); + .name(attribute.getName()) + .description(description) + .type(new GraphQLTypeReference(type)) + .build(); } - private GraphQLInputObjectField getWhereInputField(Attribute attribute) { GraphQLInputType type = getWhereAttributeType(attribute); String description = getSchemaDescription(attribute.getJavaMember()); if (type instanceof GraphQLInputType) { return GraphQLInputObjectField.newInputObjectField() - .name(attribute.getName()) - .description(description) - .type(type) - .build(); + .name(attribute.getName()) + .description(description) + .type(type) + .build(); } throw new IllegalArgumentException("Attribute " + attribute.getName() + " cannot be mapped as an Input Argument"); From ad6c2371cb2877ba2809c3f33f340fe546bbdc1f Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Tue, 11 Jun 2019 00:34:39 -0700 Subject: [PATCH 07/12] fix: add array EXISTS subquery support --- .../impl/QraphQLJpaBaseDataFetcher.java | 25 ++++++ .../GraphQLExecutorListCriteriaTests.java | 80 +++++++++++++++++++ 2 files changed, 105 insertions(+) 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 3a8dd888e..06ea2778c 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 @@ -556,6 +556,31 @@ protected Predicate getArgumentsPredicate(CriteriaBuilder cb, this.getObjectType(environment), new Field(it.getName())); boolean isOptional = false; + + if(Arrays.asList(Logical.EXISTS, Logical.NOT_EXISTS).contains(logical) ) { + AbstractQuery query = environment.getRoot(); + Subquery subquery = query.subquery(attribute.getJavaType()); + From correlation = Root.class.isInstance(path) ? subquery.correlate((Root) path) + : subquery.correlate((Join) path); + + Join correlationJoin = correlation.join(it.getName()); + + DataFetchingEnvironment existsEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment) + .root(subquery) + .build(); + + Predicate restriction = getArgumentPredicate(cb, + correlationJoin, + wherePredicateEnvironment(existsEnvironment, fieldDefinition, args), + arg); + + + Predicate exists = cb.exists(subquery.select((Join) correlationJoin) + .where(restriction)); + + return logical == Logical.EXISTS ? exists : cb.not(exists); + } + return getArgumentPredicate(cb, reuseJoin(path, it.getName(), isOptional), wherePredicateEnvironment(environment, fieldDefinition, args), diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorListCriteriaTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorListCriteriaTests.java index 123ca6ca0..029b39704 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorListCriteriaTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorListCriteriaTests.java @@ -448,6 +448,86 @@ public void queryWithWhereANDListEXISTSMixedCriteria() { assertThat(result.toString()).isEqualTo(expected); } + @Test + public void queryWithWhereBooksANDListEXISTSANDImplicitCriteria() { + //given: + String query = "query { " + + " Authors(where: {" + + " books: {" + + " NOT_EXISTS: [{" + + " author: {" + + " name: {LIKE: \"Anton\"}" + + " }" + + " } {" + + " author: {" + + " name: {LIKE: \"Igor\"}" + + " }" + + " }]" + + " }" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " }" + + "}"; + + String expected = "{Authors={select=[" + + "{id=1, name=Leo Tolstoy, books=[" + + "{id=2, title=War and Peace}, " + + "{id=3, title=Anna Karenina}]}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryWithWhereListEXISTSANDImplicitCriteria() { + //given: + String query = "query { " + + " Authors(where: {" + + " EXISTS: [{" + + " books: {" + + " title: {LIKE: \"War\"}" + + " }" + + " } {" + + " books: {" + + " title: {LIKE: \"Anna\"}" + + " }" + + " }]" + + " }) {" + + " select {" + + " id" + + " name" + + " books {" + + " id" + + " title" + + " }" + + " }" + + " }" + + "}"; + + String expected = "{Authors={select=[" + + "{id=1, name=Leo Tolstoy, books=[" + + "{id=2, title=War and Peace}, " + + "{id=3, title=Anna Karenina}]}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + @Test public void queryWithWhereANDListEXISTSCriteria() { //given: From b6e9e0bfbae35c0c010d993ca908d8541e6fa7db Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Tue, 11 Jun 2019 00:35:07 -0700 Subject: [PATCH 08/12] feat: add custom attribute EXISTS subquery test --- .../converter/GraphQLJpaConverterTests.java | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java index 2cf4cb777..d30204246 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java @@ -738,6 +738,53 @@ public void queryProcessVariablesWhereWithINListTypedValueSearchCriteria() { // then assertThat(result.toString()).isEqualTo(expected); - } + } + + + @Test + public void queryTasksVariablesWhereWithEXISTSByNameAndValueCriteria() { + //given + String query = "query {" + + " Tasks(where: {" + + " status: {EQ: COMPLETED}" + + " AND: [{" + + " EXISTS: {" + + " variables: {" + + " name: {EQ: \"variable1\"}" + + " value: {EQ: \"data\"}" + + " }" + + " }" + + " } {" + + " EXISTS: {" + + " variables: {" + + " name: {EQ: \"variable2\"}" + + " value: {EQ: true}" + + " }" + + " }" + + " }]" + + " }) {" + + " select {" + + " id" + + " status" + + " variables {" + + " name" + + " value" + + " }" + + " }" + + " }" + + "}"; + + String expected = "{Tasks={select=[" + + "{id=1, status=COMPLETED, variables=[" + + "{name=variable2, value=true}, " + + "{name=variable1, value=data}]}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } } \ No newline at end of file From 0a66de59444f2eea291dcd7b7c5c5cb81dcb6c70 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Tue, 11 Jun 2019 00:45:35 -0700 Subject: [PATCH 09/12] fix: disable show sql because of long log Travis failure --- .../src/test/resources/hibernate.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql-jpa-query-schema/src/test/resources/hibernate.properties b/graphql-jpa-query-schema/src/test/resources/hibernate.properties index 5045f52f0..d4760bdbf 100644 --- a/graphql-jpa-query-schema/src/test/resources/hibernate.properties +++ b/graphql-jpa-query-schema/src/test/resources/hibernate.properties @@ -1,6 +1,6 @@ hibernate.generate_statistics=true org.hibernate.stat=DEBUG -spring.jpa.properties.hibernate.show_sql=true +spring.jpa.properties.hibernate.show_sql=false spring.jpa.properties.hibernate.format_sql=true logging.level.org.hibernate=info From ee31c74e68a6556afa7930fcdecd9ef9cfc99aa8 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Tue, 11 Jun 2019 01:04:48 -0700 Subject: [PATCH 10/12] fix: implement missing equals() and hashCode() methods --- .../model/AbstractVariableEntity.java | 21 ++++++++++++++++ .../model/ActivitiEntityMetadata.java | 24 +++++++++++++++++++ .../model/ProcessVariableEntity.java | 16 +++++++++++++ .../jpa/query/converter/model/TaskEntity.java | 21 ++++++++++++++++ .../converter/model/TaskVariableEntity.java | 21 ++++++++++++++++ 5 files changed, 103 insertions(+) diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/AbstractVariableEntity.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/AbstractVariableEntity.java index ad6a89037..e9c6a9c0c 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/AbstractVariableEntity.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/AbstractVariableEntity.java @@ -1,6 +1,7 @@ package com.introproventures.graphql.jpa.query.converter.model; import java.util.Date; +import java.util.Objects; import javax.persistence.Column; import javax.persistence.Convert; @@ -136,4 +137,24 @@ public String getProcessInstanceId() { public void setProcessInstanceId(String processInstanceId) { this.processInstanceId = processInstanceId; } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(id); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + AbstractVariableEntity other = (AbstractVariableEntity) obj; + return Objects.equals(id, other.id); + } } \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ActivitiEntityMetadata.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ActivitiEntityMetadata.java index 9e5c4d26e..142b5f225 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ActivitiEntityMetadata.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ActivitiEntityMetadata.java @@ -1,5 +1,7 @@ package com.introproventures.graphql.jpa.query.converter.model; +import java.util.Objects; + import javax.persistence.MappedSuperclass; @MappedSuperclass @@ -75,4 +77,26 @@ public String getServiceType() { public void setServiceType(String serviceType) { this.serviceType = serviceType; } + + @Override + public int hashCode() { + return Objects.hash(appName, appVersion, serviceFullName, serviceName, serviceType, serviceVersion); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ActivitiEntityMetadata other = (ActivitiEntityMetadata) obj; + return Objects.equals(appName, other.appName) && Objects.equals(appVersion, other.appVersion) && Objects.equals( + serviceFullName, + other.serviceFullName) && Objects.equals(serviceName, + other.serviceName) && Objects.equals(serviceType, + other.serviceType) && Objects.equals(serviceVersion, + other.serviceVersion); + } } \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ProcessVariableEntity.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ProcessVariableEntity.java index 74822a46c..147f93104 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ProcessVariableEntity.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ProcessVariableEntity.java @@ -49,4 +49,20 @@ public boolean isTaskVariable() { return false; } + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + return true; + } + } \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskEntity.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskEntity.java index a02351ad9..f4e44ef43 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskEntity.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskEntity.java @@ -1,6 +1,7 @@ package com.introproventures.graphql.jpa.query.converter.model; import java.util.Date; +import java.util.Objects; import java.util.Set; import javax.persistence.ConstraintMode; @@ -373,4 +374,24 @@ public void setCompletedFrom(Date completedFrom) { this.completedFrom = completedFrom; } + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(id); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + TaskEntity other = (TaskEntity) obj; + return Objects.equals(id, other.id); + } + } diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java index 8033d905b..6a330ecdb 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java @@ -1,6 +1,7 @@ package com.introproventures.graphql.jpa.query.converter.model; import java.util.Date; +import java.util.Objects; import javax.persistence.ConstraintMode; import javax.persistence.Entity; @@ -74,6 +75,26 @@ public TaskEntity getTask() { public void setTask(TaskEntity taskEntity) { this.task = taskEntity; } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(taskId); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + TaskVariableEntity other = (TaskVariableEntity) obj; + return Objects.equals(taskId, other.taskId); + } } \ No newline at end of file From fe0fd49564833b455dc5bcf73d52002ccc9d2fe9 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Tue, 11 Jun 2019 01:05:34 -0700 Subject: [PATCH 11/12] fix: add implicit EXISTS subquery test for custom attributes --- .../converter/GraphQLJpaConverterTests.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java index d30204246..8d2d26ea6 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java @@ -742,7 +742,7 @@ public void queryProcessVariablesWhereWithINListTypedValueSearchCriteria() { @Test - public void queryTasksVariablesWhereWithEXISTSByNameAndValueCriteria() { + public void queryTasksVariablesWhereWithExplicitANDEXISTSByNameAndValueCriteria() { //given String query = "query {" + " Tasks(where: {" + @@ -786,5 +786,49 @@ public void queryTasksVariablesWhereWithEXISTSByNameAndValueCriteria() { // then assertThat(result.toString()).isEqualTo(expected); } + + + @Test + public void queryTasksVariablesWhereWithEXISTSByNameAndValueCriteria() { + //given + String query = "query {" + + " Tasks(where: {" + + " status: {EQ: COMPLETED}" + + " EXISTS: [{" + + " variables: {" + + " name: {EQ: \"variable1\"}" + + " value: {EQ: \"data\"}" + + " }" + + " } {" + + " variables: {" + + " name: {EQ: \"variable2\"}" + + " value: {EQ: true}" + + " }" + + " }]" + + " }) {" + + " select {" + + " id" + + " status" + + " variables {" + + " name" + + " value" + + " }" + + " }" + + " }" + + "}"; + + String expected = "{Tasks={select=[" + + "{id=1, status=COMPLETED, variables=[" + + "{name=variable2, value=true}, " + + "{name=variable1, value=data}]}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + } \ No newline at end of file From 23bb4488c7b5f77dba46b90e45caa37f7c286b16 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Tue, 11 Jun 2019 01:07:30 -0700 Subject: [PATCH 12/12] fix: enable openjdk11 in .travis.yaml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1d07e1bec..6cba97ed6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: required jdk: - oraclejdk8 - # - openjdk11 + - openjdk11 services: - docker