diff --git a/data-index/data-index-common/src/main/java/org/kie/kogito/index/service/ProcessDefinitionHelper.java b/data-index/data-index-common/src/main/java/org/kie/kogito/index/service/ProcessDefinitionHelper.java index 96e3b9c4ce..d32f52079d 100644 --- a/data-index/data-index-common/src/main/java/org/kie/kogito/index/service/ProcessDefinitionHelper.java +++ b/data-index/data-index-common/src/main/java/org/kie/kogito/index/service/ProcessDefinitionHelper.java @@ -29,12 +29,14 @@ import org.kie.kogito.event.process.NodeDefinition; import org.kie.kogito.event.process.ProcessDefinitionDataEvent; import org.kie.kogito.event.process.ProcessDefinitionEventBody; -import org.kie.kogito.index.CommonUtils; import org.kie.kogito.index.json.JsonUtils; import org.kie.kogito.index.model.Node; import org.kie.kogito.index.model.ProcessDefinition; +import org.kie.kogito.jackson.utils.JsonObjectUtils; +import org.kie.kogito.jackson.utils.MergeUtils; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.enterprise.context.ApplicationScoped; @@ -63,7 +65,7 @@ public static ProcessDefinition merge(ProcessDefinition instance, ProcessDefinit instance.setEndpoint(doMerge(data.getEndpoint(), instance.getEndpoint())); instance.setDescription(doMerge(data.getDescription(), instance.getDescription())); instance.setAnnotations(doMerge(data.getAnnotations(), instance.getAnnotations())); - instance.setMetadata(CommonUtils.mergeMap(toStringMap(data.getMetadata()), instance.getMetadata())); + instance.setMetadata((ObjectNode) MergeUtils.merge(JsonObjectUtils.fromValue(data.getMetadata()), instance.getMetadata())); instance.setNodes(doMerge(nodeDefinitions(data), instance.getNodes())); instance.setSource(doMerge(data.getSource(), instance.getSource())); return instance; diff --git a/data-index/data-index-graphql/src/main/resources/basic.schema.graphqls b/data-index/data-index-graphql/src/main/resources/basic.schema.graphqls index a6d8482a10..e003decceb 100644 --- a/data-index/data-index-graphql/src/main/resources/basic.schema.graphqls +++ b/data-index/data-index-graphql/src/main/resources/basic.schema.graphqls @@ -64,6 +64,7 @@ input ProcessDefinitionArgument { id: StringArgument name: StringArgument version: StringArgument + metadata: JSON } type ProcessInstance { diff --git a/data-index/data-index-storage/data-index-storage-api/src/main/java/org/kie/kogito/index/model/ProcessDefinition.java b/data-index/data-index-storage/data-index-storage-api/src/main/java/org/kie/kogito/index/model/ProcessDefinition.java index e6e2450ea0..d9c777a9f9 100644 --- a/data-index/data-index-storage/data-index-storage-api/src/main/java/org/kie/kogito/index/model/ProcessDefinition.java +++ b/data-index/data-index-storage/data-index-storage-api/src/main/java/org/kie/kogito/index/model/ProcessDefinition.java @@ -19,10 +19,11 @@ package org.kie.kogito.index.model; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; +import com.fasterxml.jackson.databind.node.ObjectNode; + public class ProcessDefinition { private String id; @@ -35,7 +36,7 @@ public class ProcessDefinition { private String source; private String description; private Set annotations; - private Map metadata; + private ObjectNode metadata; private List nodes; public String getId() { @@ -126,11 +127,11 @@ public void setAnnotations(Set annotations) { this.annotations = annotations; } - public Map getMetadata() { + public ObjectNode getMetadata() { return metadata; } - public void setMetadata(Map metadata) { + public void setMetadata(ObjectNode metadata) { this.metadata = metadata; } diff --git a/data-index/data-index-storage/data-index-storage-infinispan/src/main/java/org/kie/kogito/index/infinispan/protostream/ProcessDefinitionMarshaller.java b/data-index/data-index-storage/data-index-storage-infinispan/src/main/java/org/kie/kogito/index/infinispan/protostream/ProcessDefinitionMarshaller.java index 720fdf1e02..bd0cc4ec57 100644 --- a/data-index/data-index-storage/data-index-storage-infinispan/src/main/java/org/kie/kogito/index/infinispan/protostream/ProcessDefinitionMarshaller.java +++ b/data-index/data-index-storage/data-index-storage-infinispan/src/main/java/org/kie/kogito/index/infinispan/protostream/ProcessDefinitionMarshaller.java @@ -21,18 +21,18 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; -import java.util.Map; -import java.util.Optional; +import java.util.LinkedHashSet; import java.util.Set; -import java.util.stream.Collectors; import org.infinispan.protostream.MessageMarshaller; import org.kie.kogito.index.model.Entry; import org.kie.kogito.index.model.Node; import org.kie.kogito.index.model.ProcessDefinition; +import org.kie.kogito.jackson.utils.ObjectMapperFactory; import org.kie.kogito.persistence.infinispan.protostream.AbstractMarshaller; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; public class ProcessDefinitionMarshaller extends AbstractMarshaller implements MessageMarshaller { @@ -72,10 +72,14 @@ public ProcessDefinition readFrom(ProtoStreamReader reader) throws IOException { return pd; } - private static Map buildMetadata(ProtoStreamReader reader) throws IOException { - return Optional.ofNullable(reader.readCollection(METADATA, new HashSet<>(), Entry.class)) - .map(entries -> entries.stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue))) - .orElse(null); + private static ObjectNode buildMetadata(ProtoStreamReader reader) throws IOException { + Set set = reader.readCollection(METADATA, new HashSet<>(), Entry.class); + if (set == null) { + return null; + } + ObjectNode node = ObjectMapperFactory.get().createObjectNode(); + set.forEach(e -> node.put(e.getKey(), e.getValue())); + return node; } @Override @@ -95,10 +99,13 @@ public void writeTo(ProtoStreamWriter writer, ProcessDefinition pd) throws IOExc } private static Set buildMetadata(ProcessDefinition pd) { - return Optional.ofNullable(pd.getMetadata()) - .map(Map::entrySet) - .map(entries -> entries.stream().map(e -> new Entry(e.getKey(), e.getValue())).collect(Collectors.toSet())) - .orElse(null); + return pd.getMetadata() == null ? null : buildMetadata(pd.getMetadata()); + } + + private static Set buildMetadata(ObjectNode node) { + Set result = new LinkedHashSet(); + node.fields().forEachRemaining(e -> result.add(new Entry(e.getKey(), e.getValue().asText()))); + return result; } @Override diff --git a/data-index/data-index-storage/data-index-storage-infinispan/src/test/java/org/kie/kogito/index/infinispan/protostream/ProcessDefinitionMarshallerTest.java b/data-index/data-index-storage/data-index-storage-infinispan/src/test/java/org/kie/kogito/index/infinispan/protostream/ProcessDefinitionMarshallerTest.java index e8f08da5fb..5fd6d61e83 100644 --- a/data-index/data-index-storage/data-index-storage-infinispan/src/test/java/org/kie/kogito/index/infinispan/protostream/ProcessDefinitionMarshallerTest.java +++ b/data-index/data-index-storage/data-index-storage-infinispan/src/test/java/org/kie/kogito/index/infinispan/protostream/ProcessDefinitionMarshallerTest.java @@ -20,16 +20,16 @@ import java.io.IOException; import java.util.HashSet; -import java.util.Map; +import java.util.Set; import org.infinispan.protostream.MessageMarshaller; import org.junit.jupiter.api.Test; import org.kie.kogito.index.model.Entry; import org.kie.kogito.index.model.ProcessDefinition; +import org.kie.kogito.jackson.utils.ObjectMapperFactory; import org.mockito.InOrder; import static java.util.Collections.singleton; -import static java.util.stream.Collectors.toSet; import static org.assertj.core.api.Assertions.assertThat; import static org.kie.kogito.index.infinispan.protostream.ProcessDefinitionMarshaller.ADDONS; import static org.kie.kogito.index.infinispan.protostream.ProcessDefinitionMarshaller.ANNOTATIONS; @@ -48,6 +48,9 @@ class ProcessDefinitionMarshallerTest { + private static final String metaKey = "key1"; + private final String metaValue = "value1"; + @Test void testReadFrom() throws IOException { MessageMarshaller.ProtoStreamReader reader = mock(MessageMarshaller.ProtoStreamReader.class); @@ -56,7 +59,7 @@ void testReadFrom() throws IOException { when(reader.readString(NAME)).thenReturn("processName"); when(reader.readString(DESCRIPTION)).thenReturn("descr"); when(reader.readCollection(eq(ANNOTATIONS), any(), eq(String.class))).thenReturn(new HashSet<>(singleton("tag1"))); - when(reader.readCollection(eq(METADATA), any(), eq(Entry.class))).thenReturn(new HashSet<>(singleton(new Entry("key1", "value1")))); + when(reader.readCollection(eq(METADATA), any(), eq(Entry.class))).thenReturn(new HashSet<>(singleton(new Entry(metaKey, metaValue)))); when(reader.readCollection(eq(ROLES), any(), eq(String.class))).thenReturn(new HashSet<>(singleton("admin"))); when(reader.readCollection(eq(ADDONS), any(), eq(String.class))).thenReturn(new HashSet<>(singleton("process-management"))); when(reader.readString(TYPE)).thenReturn("processType"); @@ -71,7 +74,7 @@ void testReadFrom() throws IOException { .hasFieldOrPropertyWithValue(NAME, "processName") .hasFieldOrPropertyWithValue(DESCRIPTION, "descr") .hasFieldOrPropertyWithValue(ANNOTATIONS, singleton("tag1")) - .hasFieldOrPropertyWithValue(METADATA, Map.of("key1", "value1")) + .hasFieldOrPropertyWithValue(METADATA, ObjectMapperFactory.get().createObjectNode().put(metaKey, metaValue)) .hasFieldOrPropertyWithValue(ROLES, singleton("admin")) .hasFieldOrPropertyWithValue(ADDONS, singleton("process-management")) .hasFieldOrPropertyWithValue(TYPE, "processType"); @@ -90,13 +93,14 @@ void testReadFrom() throws IOException { @Test void testWriteTo() throws IOException { + ProcessDefinition pd = new ProcessDefinition(); pd.setId("processId"); pd.setVersion("1.0"); pd.setName("processName"); pd.setDescription("descr"); pd.setAnnotations(singleton("tag1")); - pd.setMetadata(Map.of("key1", "value1")); + pd.setMetadata(ObjectMapperFactory.get().createObjectNode().put(metaKey, metaValue)); pd.setRoles(singleton("admin")); pd.setAddons(singleton("process-management")); pd.setType("processType"); @@ -112,7 +116,7 @@ void testWriteTo() throws IOException { inOrder.verify(writer).writeString(NAME, pd.getName()); inOrder.verify(writer).writeString(DESCRIPTION, pd.getDescription()); inOrder.verify(writer).writeCollection(ANNOTATIONS, pd.getAnnotations(), String.class); - inOrder.verify(writer).writeCollection(METADATA, pd.getMetadata().entrySet().stream().map(e -> new Entry(e.getKey(), e.getValue())).collect(toSet()), Entry.class); + inOrder.verify(writer).writeCollection(METADATA, Set.of(new Entry(metaKey, metaValue)), Entry.class); inOrder.verify(writer).writeCollection(ROLES, pd.getRoles(), String.class); inOrder.verify(writer).writeCollection(ADDONS, pd.getAddons(), String.class); inOrder.verify(writer).writeString(TYPE, pd.getType()); diff --git a/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/model/ProcessDefinitionEntity.java b/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/model/ProcessDefinitionEntity.java index a7158f31bb..2b70331c1a 100644 --- a/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/model/ProcessDefinitionEntity.java +++ b/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/model/ProcessDefinitionEntity.java @@ -19,22 +19,24 @@ package org.kie.kogito.index.jpa.model; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import org.kie.kogito.index.model.ProcessDefinitionKey; +import org.kie.kogito.persistence.postgresql.hibernate.JsonBinaryConverter; + +import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.persistence.CascadeType; import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; +import jakarta.persistence.Convert; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.ForeignKey; import jakarta.persistence.Id; import jakarta.persistence.IdClass; import jakarta.persistence.JoinColumn; -import jakarta.persistence.MapKeyColumn; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; @@ -74,13 +76,9 @@ public class ProcessDefinitionEntity extends AbstractEntity { @JoinColumn(name = "process_version", referencedColumnName = "version") }, foreignKey = @ForeignKey(name = "fk_definitions_annotations")) @Column(name = "annotation") private Set annotations; - @ElementCollection - @CollectionTable(name = "definitions_metadata", joinColumns = { - @JoinColumn(name = "process_id", referencedColumnName = "id"), @JoinColumn(name = "process_version", referencedColumnName = "version") }, - foreignKey = @ForeignKey(name = "fk_definitions_metadata")) - @MapKeyColumn(name = "name") - @Column(name = "meta_value") - private Map metadata; + @Convert(converter = JsonBinaryConverter.class) + @Column(columnDefinition = "jsonb") + private ObjectNode metadata; @Override public String getId() { @@ -171,11 +169,11 @@ public void setAnnotations(Set annotations) { this.annotations = annotations; } - public Map getMetadata() { + public ObjectNode getMetadata() { return metadata; } - public void setMetadata(Map metadata) { + public void setMetadata(ObjectNode metadata) { this.metadata = metadata; } diff --git a/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/ProcessDefinitionEntityStorage.java b/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/ProcessDefinitionEntityStorage.java index e22670c665..9b64ef09b1 100644 --- a/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/ProcessDefinitionEntityStorage.java +++ b/data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/ProcessDefinitionEntityStorage.java @@ -24,11 +24,14 @@ import org.kie.kogito.index.model.ProcessDefinition; import org.kie.kogito.index.model.ProcessDefinitionKey; +import io.quarkus.arc.DefaultBean; + import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; @ApplicationScoped +@DefaultBean public class ProcessDefinitionEntityStorage extends AbstractStorage { protected ProcessDefinitionEntityStorage() { diff --git a/data-index/data-index-storage/data-index-storage-mongodb/src/main/java/org/kie/kogito/index/mongodb/model/ProcessDefinitionEntity.java b/data-index/data-index-storage/data-index-storage-mongodb/src/main/java/org/kie/kogito/index/mongodb/model/ProcessDefinitionEntity.java index b009835ade..f95dd1e7f6 100644 --- a/data-index/data-index-storage/data-index-storage-mongodb/src/main/java/org/kie/kogito/index/mongodb/model/ProcessDefinitionEntity.java +++ b/data-index/data-index-storage/data-index-storage-mongodb/src/main/java/org/kie/kogito/index/mongodb/model/ProcessDefinitionEntity.java @@ -25,6 +25,8 @@ import org.bson.codecs.pojo.annotations.BsonId; +import com.fasterxml.jackson.databind.node.ObjectNode; + public class ProcessDefinitionEntity { @BsonId @@ -40,7 +42,7 @@ public class ProcessDefinitionEntity { private Set annotations; - private Map metadata; + private ObjectNode metadata; private Set roles; @@ -148,11 +150,11 @@ public void setAnnotations(Set annotations) { this.annotations = annotations; } - public Map getMetadata() { + public ObjectNode getMetadata() { return metadata; } - public void setMetadata(Map metadata) { + public void setMetadata(ObjectNode metadata) { this.metadata = metadata; } diff --git a/data-index/data-index-storage/data-index-storage-postgresql/src/main/java/org/kie/kogito/index/postgresql/PostgresqlJsonHelper.java b/data-index/data-index-storage/data-index-storage-postgresql/src/main/java/org/kie/kogito/index/postgresql/PostgresqlJsonHelper.java index 3cb1f347d8..d8bb74a7bb 100644 --- a/data-index/data-index-storage/data-index-storage-postgresql/src/main/java/org/kie/kogito/index/postgresql/PostgresqlJsonHelper.java +++ b/data-index/data-index-storage/data-index-storage-postgresql/src/main/java/org/kie/kogito/index/postgresql/PostgresqlJsonHelper.java @@ -18,14 +18,18 @@ */ package org.kie.kogito.index.postgresql; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.kie.kogito.persistence.api.query.AttributeFilter; +import jakarta.persistence.PersistenceException; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; @@ -101,11 +105,26 @@ private static Expression buildObjectExpression(CriteriaBuilder builder, Object private static Expression buildPathExpression(CriteriaBuilder builder, Root root, String attributeName, boolean isStr) { String[] attributes = attributeName.split("\\."); - Expression[] arguments = new Expression[attributes.length]; - arguments[0] = root.get(attributes[0]); - for (int i = 1; i < attributes.length; i++) { - arguments[i] = builder.literal(attributes[i]); + + Collection arguments = new ArrayList<>(); + if (attributes.length == 1) + return root.get(attributeName); + int startIndex; + // Check if the first attribute is a join, if it is, assume next attribute is the json property (not sure thats necessarily correct but it will work) + try { + Join join = root.join(attributes[0]); + arguments.add(join.get(attributes[1])); + startIndex = 2; + } catch (PersistenceException ex) { + // If not, the first attribute is the json one, + arguments.add(root.get(attributes[0])); + startIndex = 1; + } + + for (int i = startIndex; i < attributes.length; i++) { + arguments.add(builder.literal(attributes[i])); } - return isStr ? builder.function("jsonb_extract_path_text", String.class, arguments) : builder.function("jsonb_extract_path", Object.class, arguments); + return isStr ? builder.function("jsonb_extract_path_text", String.class, arguments.toArray(new Expression[arguments.size()])) + : builder.function("jsonb_extract_path", Object.class, arguments.toArray(new Expression[arguments.size()])); } } diff --git a/data-index/data-index-storage/data-index-storage-postgresql/src/main/java/org/kie/kogito/index/postgresql/PostgresqlProcessDefinitionEntityStorage.java b/data-index/data-index-storage/data-index-storage-postgresql/src/main/java/org/kie/kogito/index/postgresql/PostgresqlProcessDefinitionEntityStorage.java new file mode 100644 index 0000000000..03b25e9329 --- /dev/null +++ b/data-index/data-index-storage/data-index-storage-postgresql/src/main/java/org/kie/kogito/index/postgresql/PostgresqlProcessDefinitionEntityStorage.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.kie.kogito.index.postgresql; + +import org.kie.kogito.index.jpa.mapper.ProcessDefinitionEntityMapper; +import org.kie.kogito.index.jpa.model.ProcessDefinitionEntityRepository; +import org.kie.kogito.index.jpa.storage.ProcessDefinitionEntityStorage; +import org.kie.kogito.index.model.ProcessDefinition; +import org.kie.kogito.persistence.api.query.Query; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class PostgresqlProcessDefinitionEntityStorage extends ProcessDefinitionEntityStorage { + + @Inject + public PostgresqlProcessDefinitionEntityStorage(ProcessDefinitionEntityRepository repository, ProcessDefinitionEntityMapper mapper) { + super(repository, mapper); + } + + @Override + public Query query() { + return new PostgresqlJsonJPAQuery<>(repository, mapToModel, entityClass); + } +} diff --git a/data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/kie-flyway/db/data-index/postgresql/V1.45.1.0__metadata_as_jsonb.sql b/data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/kie-flyway/db/data-index/postgresql/V1.45.1.0__metadata_as_jsonb.sql new file mode 100644 index 0000000000..4f24af5578 --- /dev/null +++ b/data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/kie-flyway/db/data-index/postgresql/V1.45.1.0__metadata_as_jsonb.sql @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +ALTER TABLE definitions ADD COLUMN metadata jsonb; + +UPDATE definitions SET metadata = +(SELECT json_object_agg(name, meta_value) FROM definitions_metadata where process_id = a.process_id and process_version = a.process_version) +FROM (SELECT * FROM definitions_metadata) as a +WHERE process_id = a.process_id and process_version = a.process_version; + +DROP TABLE definitions_metadata;