From ba2bf7d9b2c25309d7d6ba4160d4e36357555a95 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 22 Apr 2019 12:40:13 -0700 Subject: [PATCH 1/3] feat: add LOCATE predicate for attributes annotated with @Convert --- .gitignore | 3 + graphql-jpa-query-schema/pom.xml | 6 + .../schema/impl/GraphQLJpaSchemaBuilder.java | 17 +- .../schema/impl/JpaPredicateBuilder.java | 3 + .../query/schema/impl/PredicateFilter.java | 7 +- .../converter/GraphQLJpaConverterTests.java | 316 ++++++++++++++++++ .../model/AbstractVariableEntity.java | 139 ++++++++ .../model/ActivitiEntityMetadata.java | 78 +++++ .../jpa/query/converter/model/JsonEntity.java | 28 ++ .../converter/model/JsonNodeConverter.java | 46 +++ .../model/ProcessVariableEntity.java | 52 +++ .../converter/model/TaskVariableEntity.java | 58 ++++ .../query/converter/model/VariableValue.java | 58 ++++ .../model/VariableValueJsonConverter.java | 39 +++ .../resources/GraphQLJpaConverterTests.sql | 15 + 15 files changed, 860 insertions(+), 5 deletions(-) create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/AbstractVariableEntity.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ActivitiEntityMetadata.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/JsonEntity.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/JsonNodeConverter.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ProcessVariableEntity.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/VariableValue.java create mode 100644 graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/VariableValueJsonConverter.java create mode 100644 graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql diff --git a/.gitignore b/.gitignore index bcebd1c9a..038c2b9dd 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ target/ .project .springBeans .classpath + +.externalToolBuilders + diff --git a/graphql-jpa-query-schema/pom.xml b/graphql-jpa-query-schema/pom.xml index 77855d9eb..0eefeb62c 100644 --- a/graphql-jpa-query-schema/pom.xml +++ b/graphql-jpa-query-schema/pom.xml @@ -60,6 +60,12 @@ test + + com.fasterxml.jackson.core + jackson-databind + test + + \ No newline at end of file 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 20c07785d..cb58177cd 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 @@ -33,6 +33,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.persistence.Convert; import javax.persistence.EntityManager; import javax.persistence.Transient; import javax.persistence.metamodel.Attribute; @@ -43,9 +44,6 @@ import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.introproventures.graphql.jpa.query.annotation.GraphQLDescription; import com.introproventures.graphql.jpa.query.annotation.GraphQLIgnore; import com.introproventures.graphql.jpa.query.annotation.GraphQLIgnoreFilter; @@ -55,7 +53,6 @@ import com.introproventures.graphql.jpa.query.schema.NamingStrategy; import com.introproventures.graphql.jpa.query.schema.impl.IntrospectionUtils.CachedIntrospectionResult.CachedPropertyDescriptor; import com.introproventures.graphql.jpa.query.schema.impl.PredicateFilter.Criteria; - import graphql.Assert; import graphql.Scalars; import graphql.schema.Coercing; @@ -73,6 +70,8 @@ import graphql.schema.GraphQLType; import graphql.schema.GraphQLTypeReference; import graphql.schema.PropertyDataFetcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * JPA specific schema builder implementation of {code #GraphQLSchemaBuilder} interface @@ -437,6 +436,16 @@ private GraphQLInputType getWhereAttributeType(Attribute attribute) { .type(getAttributeInputType(attribute)) .build() ); + } + else if (attribute.getJavaMember().getClass().isAssignableFrom(Field.class) + && Field.class.cast(attribute.getJavaMember()) + .isAnnotationPresent(Convert.class)) + { + builder.field(GraphQLInputObjectField.newInputObjectField() + .name(Criteria.LOCATE.name()) + .description("Locate search criteria") + .type(getAttributeInputType(attribute)) + .build()); } } diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java index 13cf27236..32f0e6930 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java @@ -414,6 +414,9 @@ else if(Collection.class.isAssignableFrom(type)) { } else if(type.isEnum()) { return getEnumPredicate((Path>) field, predicateFilter); } + else if (filter.getCriterias().contains(PredicateFilter.Criteria.LOCATE)) { + return cb.gt(cb.locate(from.get(filter.getField()), value.toString()), 0); + } throw new IllegalArgumentException("Unsupported field type " + type + " for field " + predicateFilter.getField()); } diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/PredicateFilter.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/PredicateFilter.java index 7e14bcbbc..8e7080ced 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/PredicateFilter.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/PredicateFilter.java @@ -103,7 +103,12 @@ public enum Criteria { /** * Not Between condition */ - NOT_BETWEEN; + NOT_BETWEEN, + + /** + * JPA's LOCATE predicate for attributes annotated with @Convert + */ + LOCATE; private static Set names = EnumSet.allOf(Criteria.class) .stream() 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 new file mode 100644 index 000000000..6e9db3674 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/GraphQLJpaConverterTests.java @@ -0,0 +1,316 @@ +/* + * Copyright 2017 IntroPro Ventures Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.introproventures.graphql.jpa.query.converter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.Query; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; +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.VariableValue; +import com.introproventures.graphql.jpa.query.schema.GraphQLExecutor; +import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder; +import com.introproventures.graphql.jpa.query.schema.JavaScalars; +import com.introproventures.graphql.jpa.query.schema.JavaScalars.GraphQLObjectCoercing; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; +import graphql.schema.GraphQLScalarType; +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; + + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment=WebEnvironment.NONE, + properties = "spring.datasource.data=GraphQLJpaConverterTests.sql") +@TestPropertySource({"classpath:hibernate.properties"}) +public class GraphQLJpaConverterTests { + + @SpringBootApplication + static class Application { + @Bean + public GraphQLExecutor graphQLExecutor(final GraphQLSchemaBuilder graphQLSchemaBuilder) { + return new GraphQLJpaExecutor(graphQLSchemaBuilder.build()); + } + + @Bean + public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManager entityManager) { + + JavaScalars.register(JsonNode.class, new GraphQLScalarType("Json", "Json type", new GraphQLObjectCoercing())); + JavaScalars.register(VariableValue.class, new GraphQLScalarType("VariableValue", "VariableValue Type", new GraphQLObjectCoercing())); + + return new GraphQLJpaSchemaBuilder(entityManager) + .name("HashMapSchema") + .description("Json Entity test schema"); + } + + } + + @Autowired + private GraphQLExecutor executor; + + @Autowired + private EntityManager entityManager; + + @Test + public void contextLoads() { + + } + + @Test + @Transactional + public void queryTester() { + // given: + Query query = entityManager.createQuery("select json from JsonEntity json where json.attributes LIKE '%key%'"); + + // when: + List result = query.getResultList(); + + // then: + assertThat(result).isNotEmpty(); + assertThat(result).hasSize(1); + } + + @Test + @Transactional + public void criteriaTester() { + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteria = builder.createQuery(JsonEntity.class); + Root json = criteria.from(JsonEntity.class); + + JsonNode value = new ObjectMapper().valueToTree(Collections.singletonMap("value", + new String[] {"1","2","3","4","5"})); + criteria.select(json) + .where(builder.equal(json.get("attributes"), value)); + + // when: + List result = entityManager.createQuery(criteria).getResultList(); + + // then: + assertThat(result).isNotEmpty(); + assertThat(result).hasSize(1); + } + + @Test // Problem with generating cast() in the where expression + @Transactional + public void criteriaTesterLike() { + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteria = builder.createQuery(JsonEntity.class); + Root json = criteria.from(JsonEntity.class); + + criteria.select(json) + .where(builder.like(json.get("attributes").as(String.class), "%key%")); + + // when: + List result = entityManager.createQuery(criteria).getResultList(); + + // then: + assertThat(result).isNotEmpty(); + assertThat(result).hasSize(1); + } + + + @Test + @Transactional + public void criteriaTesterLocate() { + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteria = builder.createQuery(JsonEntity.class); + Root json = criteria.from(JsonEntity.class); + + criteria.select(json) + .where(builder.gt(builder.locate(json.get("attributes"),"key"), 0)); + + // when: + List result = entityManager.createQuery(criteria).getResultList(); + + // then: + assertThat(result).isNotEmpty(); + assertThat(result).hasSize(1); + } + + @Test + public void queryJsonEntity() { + //given + String query = "query {" + + " JsonEntities {" + + " select {" + + " id" + + " firstName" + + " lastName" + + " attributes" + + " }" + + " }" + + "}"; + + String expected = "{JsonEntities={select=[" + + "{id=1, firstName=john, lastName=doe, attributes={\"value\":{\"key\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}}, " + + "{id=2, firstName=joe, lastName=smith, attributes={\"value\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryJsonEntityWhereSearchCriteria() { + //given + String query = "query {" + + " JsonEntities(where: {" + + "attributes: {LOCATE: \"key\"}" + + "}) {" + + " select {" + + " id" + + " firstName" + + " lastName" + + " attributes" + + " }" + + " }" + + "}"; + + String expected = "{JsonEntities={select=[" + + "{id=1, firstName=john, lastName=doe, attributes={\"value\":{\"key\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}}" + + "]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryTaskVariablesWhereSearchCriteria() { + //given + String query = "query {" + + " TaskVariables(where: {" + + "value: {LOCATE: \"true\"}" + + "}) {" + + " select {" + + " id" + + " name" + + " value" + + " }" + + " }" + + "}"; + + String expected = "{TaskVariables={select=[{id=2, name=variable2, value=true}]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryTaskVariablesWhereSearchCriteriaVariableBinding() { + //given + String query = "query($value: VariableValue!) {" + + " TaskVariables(where: {" + + "value: {LOCATE: $value }" + + "}) {" + + " select {" + + " id" + + " name" + + " value" + + " }" + + " }" + + "}"; + + Map variables = Collections.singletonMap("value", true); + + String expected = "{TaskVariables={select=[{id=2, name=variable2, value=true}]}}"; + + //when + Object result = executor.execute(query, variables).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + + @Test + public void queryProcessVariablesWhereSearchCriteriaVariableBindings() { + //given + String query = "query($value: VariableValue!) {" + + " ProcessVariables(where: {" + + "value: {LOCATE: $value}" + + "}) {" + + " select {" + + " id" + + " name" + + " value" + + " }" + + " }" + + "}"; + + Map variables = Collections.singletonMap("value", "[\"1\",\"2\",\"3\",\"4\",\"5\"]"); + + String expected = "{ProcessVariables={select=[{id=1, name=document, value={key=[1, 2, 3, 4, 5]}}]}}"; + + //when + Object result = executor.execute(query, variables).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryProcessVariablesWhereSearchCriteria() { + //given + String query = "query {" + + " ProcessVariables(where: {" + + "value: {LOCATE: \"[\\\"1\\\",\\\"2\\\",\\\"3\\\",\\\"4\\\",\\\"5\\\"]\"}" + + "}) {" + + " select {" + + " id" + + " name" + + " value" + + " }" + + " }" + + "}"; + + String expected = "{ProcessVariables={select=[{id=1, name=document, value={key=[1, 2, 3, 4, 5]}}]}}"; + + //when + Object result = executor.execute(query).getData(); + + // then + assertThat(result.toString()).isEqualTo(expected); + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..ad6a89037 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/AbstractVariableEntity.java @@ -0,0 +1,139 @@ +package com.introproventures.graphql.jpa.query.converter.model; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +import org.springframework.format.annotation.DateTimeFormat; + +@MappedSuperclass +public abstract class AbstractVariableEntity extends ActivitiEntityMetadata { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String type; + + private String name; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) + private Date createTime; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) + private Date lastUpdatedTime; + + private String executionId; + + @Convert(converter = VariableValueJsonConverter.class) + @Column(columnDefinition="text") + private VariableValue value; + + private Boolean markedAsDeleted = false; + + private String processInstanceId; + + public AbstractVariableEntity() { + } + + public AbstractVariableEntity(Long id, + String type, + String name, + String processInstanceId, + String serviceName, + String serviceFullName, + String serviceVersion, + String appName, + String appVersion, + Date createTime, + Date lastUpdatedTime, + String executionId) { + super(serviceName, + serviceFullName, + serviceVersion, + appName, + appVersion); + this.id = id; + this.type = type; + this.name = name; + this.processInstanceId = processInstanceId; + this.createTime = createTime; + this.lastUpdatedTime = lastUpdatedTime; + this.executionId = executionId; + } + + public Long getId() { + return id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getLastUpdatedTime() { + return lastUpdatedTime; + } + + public void setLastUpdatedTime(Date lastUpdatedTime) { + this.lastUpdatedTime = lastUpdatedTime; + } + + public String getExecutionId() { + return executionId; + } + + public void setExecutionId(String executionId) { + this.executionId = executionId; + } + + public void setValue(T value) { + this.value = new VariableValue<>(value); + } + + public T getValue() { + return (T) value.getValue(); + } + + public Boolean getMarkedAsDeleted() { + return markedAsDeleted; + } + + public void setMarkedAsDeleted(Boolean markedAsDeleted) { + this.markedAsDeleted = markedAsDeleted; + } + + + public String getProcessInstanceId() { + return processInstanceId; + } + + + public void setProcessInstanceId(String processInstanceId) { + this.processInstanceId = processInstanceId; + } + } \ 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 new file mode 100644 index 000000000..9e5c4d26e --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ActivitiEntityMetadata.java @@ -0,0 +1,78 @@ +package com.introproventures.graphql.jpa.query.converter.model; + +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class ActivitiEntityMetadata { + + protected String serviceName; + protected String serviceFullName; + protected String serviceVersion; + protected String appName; + protected String appVersion; + protected String serviceType; + + public ActivitiEntityMetadata() { + + } + + public ActivitiEntityMetadata(String serviceName, + String serviceFullName, + String serviceVersion, + String appName, + String appVersion) { + this.serviceName = serviceName; + this.serviceFullName = serviceFullName; + this.serviceVersion = serviceVersion; + this.appName = appName; + this.appVersion = appVersion; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getServiceFullName() { + return serviceFullName; + } + + public void setServiceFullName(String serviceFullName) { + this.serviceFullName = serviceFullName; + } + + public String getServiceVersion() { + return serviceVersion; + } + + public void setServiceVersion(String serviceVersion) { + this.serviceVersion = serviceVersion; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getAppVersion() { + return appVersion; + } + + public void setAppVersion(String appVersion) { + this.appVersion = appVersion; + } + + public String getServiceType() { + return serviceType; + } + + public void setServiceType(String serviceType) { + this.serviceType = serviceType; + } +} \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/JsonEntity.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/JsonEntity.java new file mode 100644 index 000000000..87e161a18 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/JsonEntity.java @@ -0,0 +1,28 @@ +package com.introproventures.graphql.jpa.query.converter.model; + +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; + +@Entity(name = "JsonEntity") +@Table(name = "json_entity") +@Data +public class JsonEntity { + + @Id + private int id; + + private String firstName; + + private String lastName; + + @Convert(converter = JsonNodeConverter.class) + @Column(columnDefinition = "text") + private JsonNode attributes; + +} \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/JsonNodeConverter.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/JsonNodeConverter.java new file mode 100644 index 000000000..2a509db2e --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/JsonNodeConverter.java @@ -0,0 +1,46 @@ +package com.introproventures.graphql.jpa.query.converter.model; + +import java.io.IOException; + +import javax.persistence.AttributeConverter; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JsonNodeConverter implements AttributeConverter { + private final static Logger logger = LoggerFactory.getLogger(JsonNodeConverter.class); + + private static final ObjectMapper objectMapper = new ObjectMapper() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + @Override + public String convertToDatabaseColumn(JsonNode jsonMap) { + + String jsonString = null; + try { + jsonString = objectMapper.writeValueAsString(jsonMap); + } catch (final JsonProcessingException e) { + logger.error("JSON writing error", e); + } + + return jsonString; + } + + @Override + public JsonNode convertToEntityAttribute(String jsonString) { + + JsonNode jsonMap = null; + try { + jsonMap = objectMapper.readValue(jsonString, JsonNode.class); + + } catch (final IOException e) { + logger.error("JSON reading error", e); + } + + return jsonMap; + } + +} \ 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 new file mode 100644 index 000000000..74822a46c --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/ProcessVariableEntity.java @@ -0,0 +1,52 @@ +package com.introproventures.graphql.jpa.query.converter.model; + + + +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity(name="ProcessVariable") +@Table(name = "PROCESS_VARIABLE") +public class ProcessVariableEntity extends AbstractVariableEntity { + + public ProcessVariableEntity() { + } + + public ProcessVariableEntity(Long id, + String type, + String name, + String processInstanceId, + String serviceName, + String serviceFullName, + String serviceVersion, + String appName, + String appVersion, + Date createTime, + Date lastUpdatedTime, + String executionId) { + super(id, + type, + name, + processInstanceId, + serviceName, + serviceFullName, + serviceVersion, + appName, + appVersion, + createTime, + lastUpdatedTime, + executionId); + + } + + public String getTaskId() { + return null; + } + + public boolean isTaskVariable() { + return false; + } + +} \ 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 new file mode 100644 index 000000000..9ea3365b0 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/TaskVariableEntity.java @@ -0,0 +1,58 @@ +package com.introproventures.graphql.jpa.query.converter.model; + +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity(name="TaskVariable") +@Table(name = "TASK_VARIABLE") +public class TaskVariableEntity extends AbstractVariableEntity { + + private String taskId; + + public TaskVariableEntity() { + } + + public TaskVariableEntity(Long id, + String type, + String name, + String processInstanceId, + String serviceName, + String serviceFullName, + String serviceVersion, + String appName, + String appVersion, + String taskId, + Date createTime, + Date lastUpdatedTime, + String executionId) { + super(id, + type, + name, + processInstanceId, + serviceName, + serviceFullName, + serviceVersion, + appName, + appVersion, + createTime, + lastUpdatedTime, + executionId); + + this.taskId = taskId; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public boolean isTaskVariable() { + 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/VariableValue.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/VariableValue.java new file mode 100644 index 000000000..23b4e14b9 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/VariableValue.java @@ -0,0 +1,58 @@ +package com.introproventures.graphql.jpa.query.converter.model; + + +public class VariableValue { + + private T value; + + public VariableValue() { + } + + public VariableValue(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + + + /** + * Encountered Java type [class org.activiti.cloud.services.query.model.VariableValue] for which we could not locate a JavaTypeDescriptor + * and which does not appear to implement equals and/or hashCode. This can lead to significant performance problems when performing + * equality/dirty checking involving this Java type. + * + * Consider registering a custom JavaTypeDescriptor or at least implementing equals/hashCode. + * + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VariableValue other = (VariableValue) obj; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + @Override + public String toString() { + return "VariableValue [value=" + value + "]"; + } + +} \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/VariableValueJsonConverter.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/VariableValueJsonConverter.java new file mode 100644 index 000000000..75211e219 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/converter/model/VariableValueJsonConverter.java @@ -0,0 +1,39 @@ +package com.introproventures.graphql.jpa.query.converter.model; + + +import java.io.IOException; + +import javax.persistence.AttributeConverter; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hibernate.QueryException; + +public class VariableValueJsonConverter implements AttributeConverter, String> { + + private static ObjectMapper objectMapper = new ObjectMapper() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + + public VariableValueJsonConverter() { + } + + @Override + public String convertToDatabaseColumn(VariableValue variableValue) { + try { + return objectMapper.writeValueAsString(variableValue); + } catch (JsonProcessingException e) { + throw new QueryException("Unable to serialize variable.", e); + } + } + + @Override + public VariableValue convertToEntityAttribute(String dbData) { + try { + return objectMapper.readValue(dbData, VariableValue.class); + } catch (IOException e) { + throw new QueryException("Unable to deserialize variable.", e); + } + } + +} \ 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 new file mode 100644 index 000000000..bffdbe407 --- /dev/null +++ b/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql @@ -0,0 +1,15 @@ +-- Json entity +insert into json_entity (id, first_name, last_name, attributes) values + (1, 'john', 'doe', '{"value":{"key":["1","2","3","4","5"]}}'), + (2, 'joe', 'smith', '{"value":["1","2","3","4","5"]}'); + +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"]}}'); + +insert into TASK_VARIABLE (create_time, execution_id, last_updated_time, name, process_instance_id, task_id, type, value) values + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable1', 0, '1', 'string', '{"value":"data"}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable2', 0, '1', 'boolean', '{"value":true}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable3', 0, '2', 'string', '{"value":null}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable4', 0, '2', 'json', '{"value":{"key":"data"}}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable5', 1, '4', 'double', '{"value":1.0}'), + (CURRENT_TIMESTAMP, 'execution_id', CURRENT_TIMESTAMP, 'variable6', 1, '4', 'json', '{"value":[1,2,3,4,5]}'); \ No newline at end of file From a1e9b7176d4e4ade882087bdf0f0a721c3e8ec09 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 22 Apr 2019 12:49:56 -0700 Subject: [PATCH 2/3] fix: polish test data --- .../jpa/query/converter/GraphQLJpaConverterTests.java | 8 ++++---- .../src/test/resources/GraphQLJpaConverterTests.sql | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) 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 6e9db3674..fa0e2ea2f 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 @@ -109,7 +109,7 @@ public void criteriaTester() { CriteriaQuery criteria = builder.createQuery(JsonEntity.class); Root json = criteria.from(JsonEntity.class); - JsonNode value = new ObjectMapper().valueToTree(Collections.singletonMap("value", + JsonNode value = new ObjectMapper().valueToTree(Collections.singletonMap("attr", new String[] {"1","2","3","4","5"})); criteria.select(json) .where(builder.equal(json.get("attributes"), value)); @@ -174,8 +174,8 @@ public void queryJsonEntity() { "}"; String expected = "{JsonEntities={select=[" - + "{id=1, firstName=john, lastName=doe, attributes={\"value\":{\"key\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}}, " - + "{id=2, firstName=joe, lastName=smith, attributes={\"value\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}" + + "{id=1, firstName=john, lastName=doe, attributes={\"attr\":{\"key\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}}, " + + "{id=2, firstName=joe, lastName=smith, attributes={\"attr\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}" + "]}}"; //when @@ -202,7 +202,7 @@ public void queryJsonEntityWhereSearchCriteria() { "}"; String expected = "{JsonEntities={select=[" - + "{id=1, firstName=john, lastName=doe, attributes={\"value\":{\"key\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}}" + + "{id=1, firstName=john, lastName=doe, attributes={\"attr\":{\"key\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}}}" + "]}}"; //when diff --git a/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql b/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql index bffdbe407..28a15be2d 100644 --- a/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql +++ b/graphql-jpa-query-schema/src/test/resources/GraphQLJpaConverterTests.sql @@ -1,7 +1,7 @@ -- Json entity insert into json_entity (id, first_name, last_name, attributes) values - (1, 'john', 'doe', '{"value":{"key":["1","2","3","4","5"]}}'), - (2, 'joe', 'smith', '{"value":["1","2","3","4","5"]}'); + (1, 'john', 'doe', '{"attr":{"key":["1","2","3","4","5"]}}'), + (2, 'joe', 'smith', '{"attr":["1","2","3","4","5"]}'); 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 934872e3212d1a56eb6d2fd920187a228d73595c Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 22 Apr 2019 13:15:51 -0700 Subject: [PATCH 3/3] fix: clean up old code. --- .../jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java index acaddfaae..16d570b27 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java @@ -66,10 +66,6 @@ public Object get(DataFetchingEnvironment environment) { //EntityGraph entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field)))); - // Let's clear session persistent context to avoid getting stale objects cached in the same session - // between requests with different search criteria. This looks like a Hibernate bug... - entityManager.clear(); - return getQuery(environment, field, true) //.setHint("javax.persistence.fetchgraph", entityGraph) // TODO: fix runtime exception .getResultList();