This is an automated email from the ASF dual-hosted git repository.
shuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/master by this push:
new f2fb628 UNOMI-365 Create new Unomi Topic object & queries & mutations
(#189)
f2fb628 is described below
commit f2fb62886648fffa32469fc1016a5d65a289865a
Author: anatol-sialitski <[email protected]>
AuthorDate: Tue Sep 22 11:46:39 2020 +0300
UNOMI-365 Create new Unomi Topic object & queries & mutations (#189)
* UNOMI-365 Create new Unomi Topic object
* UNOMI-365 Create new Unomi Topic object
* resolved conflicts
* UNOMI-365 Create new Unomi Topic object
---
.../src/main/java/org/apache/unomi/api/Topic.java | 34 ++++++---
.../apache/unomi/api/services/TopicService.java | 57 +++++++++++++++
.../commands/CreateOrUpdateTopicCommand.java | 25 ++++++-
.../unomi/graphql/commands/DeleteTopicCommand.java | 7 +-
.../condition/factories/TopicConditionFactory.java | 78 +++++++++++++++++++++
.../fetchers/FindTopicsConnectionDataFetcher.java | 25 ++++++-
.../unomi/graphql/fetchers/TopicDataFetcher.java | 14 +++-
.../unomi/graphql/types/output/CDPTopic.java | 17 ++++-
.../graphql/types/output/CDPTopicConnection.java | 25 +++++--
.../unomi/graphql/types/output/CDPTopicEdge.java | 11 ++-
.../unomi/itests/graphql/GraphQLTopicIT.java | 78 +++++++++++++++++++++
.../test/resources/graphql/topic/create-topic.json | 11 +++
.../test/resources/graphql/topic/delete-topic.json | 7 ++
.../test/resources/graphql/topic/find-topics.json | 25 +++++++
.../test/resources/graphql/topic/get-topic.json | 7 ++
.../test/resources/graphql/topic/update-topic.json | 11 +++
.../resources/META-INF/cxs/mappings/topic.json | 20 ++++++
.../unomi/persistence/spi/CustomObjectMapper.java | 1 +
.../cxs/conditions/topicPropertyCondition.json | 31 +++++++++
.../services/impl/topics/TopicServiceImpl.java | 81 ++++++++++++++++++++++
.../resources/OSGI-INF/blueprint/blueprint.xml | 12 ++++
21 files changed, 551 insertions(+), 26 deletions(-)
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/TopicDataFetcher.java
b/api/src/main/java/org/apache/unomi/api/Topic.java
similarity index 60%
copy from
graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/TopicDataFetcher.java
copy to api/src/main/java/org/apache/unomi/api/Topic.java
index 05ba1af..03d789d 100644
---
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/TopicDataFetcher.java
+++ b/api/src/main/java/org/apache/unomi/api/Topic.java
@@ -14,24 +14,38 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.unomi.graphql.fetchers;
+package org.apache.unomi.api;
-import graphql.schema.DataFetcher;
-import graphql.schema.DataFetchingEnvironment;
-import org.apache.unomi.graphql.types.output.CDPTopic;
+public class Topic extends Item {
-public class TopicDataFetcher implements DataFetcher<CDPTopic> {
+ public static final String ITEM_TYPE = "topic";
- private final String topicId;
+ private String topicId;
- public TopicDataFetcher(final String topicId) {
+ private String name;
+
+ public String getTopicId() {
+ return topicId;
+ }
+
+ public void setTopicId(String topicId) {
this.topicId = topicId;
}
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
@Override
- public CDPTopic get(DataFetchingEnvironment environment) throws Exception {
- // Unomi doesn't have an API for that yet, so return a stub
- return null;
+ public String toString() {
+ return "Topic{" +
+ "topicId='" + topicId + '\'' +
+ ", name='" + name + '\'' +
+ '}';
}
}
diff --git a/api/src/main/java/org/apache/unomi/api/services/TopicService.java
b/api/src/main/java/org/apache/unomi/api/services/TopicService.java
new file mode 100644
index 0000000..6cb5e0b
--- /dev/null
+++ b/api/src/main/java/org/apache/unomi/api/services/TopicService.java
@@ -0,0 +1,57 @@
+/*
+ * 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.apache.unomi.api.services;
+
+import org.apache.unomi.api.PartialList;
+import org.apache.unomi.api.Topic;
+import org.apache.unomi.api.query.Query;
+
+public interface TopicService {
+
+ /**
+ * Retrieves the topic identified by the specified identifier.
+ *
+ * @param topicId the identifier of the topic to retrieve
+ * @return the topic identified by the specified identifier or {@code
null} if no such topic exists
+ */
+ Topic load(final String topicId);
+
+ /**
+ * Saves the specified topic in the context server.
+ *
+ * @param topic the topic to be saved
+ * @return the newly saved topic if the creation or update was successful,
{@code null} otherwise
+ */
+ Topic save(final Topic topic);
+
+ /**
+ * Retrieves topic matching the specified query.
+ *
+ * @param query a {@link Query} specifying which elements to retrieve
+ * @return a {@link PartialList} of {@link Topic} metadata
+ */
+ PartialList<Topic> search(final Query query);
+
+ /**
+ * Removes the topic identified by the specified identifier.
+ *
+ * @param topicId the identifier of the profile or persona to delete
+ * @return {@code true} if the deletion was successful, {@code false}
otherwise
+ */
+ boolean delete(final String topicId);
+
+}
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/CreateOrUpdateTopicCommand.java
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/CreateOrUpdateTopicCommand.java
index 22c41c2..835b613 100644
---
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/CreateOrUpdateTopicCommand.java
+++
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/CreateOrUpdateTopicCommand.java
@@ -16,6 +16,9 @@
*/
package org.apache.unomi.graphql.commands;
+import com.google.common.base.Strings;
+import org.apache.unomi.api.Topic;
+import org.apache.unomi.api.services.TopicService;
import org.apache.unomi.graphql.types.input.CDPTopicInput;
import org.apache.unomi.graphql.types.output.CDPTopic;
@@ -33,8 +36,26 @@ public class CreateOrUpdateTopicCommand extends
BaseCommand<CDPTopic> {
@Override
public CDPTopic execute() {
- // Unomi doesn't have an API for that yet, so return a stub
- return new CDPTopic();
+ final TopicService topicService =
serviceManager.getService(TopicService.class);
+
+ Topic topic = topicService.load(topicInput.getId());
+
+ if (topic == null) {
+ topic = new Topic();
+ }
+
+ final String topicId = Strings.isNullOrEmpty(topicInput.getId())
+ ? topicInput.getName()
+ : topicInput.getId();
+
+ topic.setTopicId(topicId);
+ topic.setItemId(topicId);
+ topic.setName(topicInput.getName());
+ topic.setScope(topicInput.getView());
+
+ Topic storedTopic = topicService.save(topic);
+
+ return new CDPTopic(storedTopic);
}
public static Builder create(final CDPTopicInput topicInput) {
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/DeleteTopicCommand.java
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/DeleteTopicCommand.java
index c8fa304..33fc7cd 100644
---
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/DeleteTopicCommand.java
+++
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/DeleteTopicCommand.java
@@ -16,6 +16,8 @@
*/
package org.apache.unomi.graphql.commands;
+import org.apache.unomi.api.services.TopicService;
+
import java.util.Objects;
public class DeleteTopicCommand extends BaseCommand<Boolean> {
@@ -30,8 +32,9 @@ public class DeleteTopicCommand extends BaseCommand<Boolean> {
@Override
public Boolean execute() {
- // Unomi doesn't have an API for that yet, so return a stub
- return false;
+ final TopicService topicService =
serviceManager.getService(TopicService.class);
+
+ return topicService.delete(topicId);
}
public static Builder create(final String topicId) {
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/condition/factories/TopicConditionFactory.java
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/condition/factories/TopicConditionFactory.java
new file mode 100644
index 0000000..9c68361
--- /dev/null
+++
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/condition/factories/TopicConditionFactory.java
@@ -0,0 +1,78 @@
+/*
+ * 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.apache.unomi.graphql.condition.factories;
+
+import graphql.schema.DataFetchingEnvironment;
+import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.graphql.types.input.CDPTopicFilterInput;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class TopicConditionFactory extends ConditionFactory {
+
+ private static TopicConditionFactory instance;
+
+ public static synchronized TopicConditionFactory get(final
DataFetchingEnvironment environment) {
+ if (instance == null) {
+ instance = new TopicConditionFactory(environment);
+ }
+
+ return instance;
+ }
+
+ private TopicConditionFactory(final DataFetchingEnvironment environment) {
+ super("topicPropertyCondition", environment);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Condition filterInputCondition(final CDPTopicFilterInput
filterInput, final Map<String, Object> filterInputAsMap) {
+ if (filterInput == null) {
+ return matchAllCondition();
+ }
+
+ final List<Condition> rootSubConditions = new ArrayList<>();
+
+ if (filterInput.getId_equals() != null) {
+ rootSubConditions.add(propertyCondition("itemId",
filterInput.getId_equals()));
+ }
+
+ if (filterInput.getName_equals() != null) {
+ rootSubConditions.add(propertyCondition("name",
filterInput.getName_equals()));
+ }
+
+ if (filterInput.getView_equals() != null) {
+ rootSubConditions.add(propertyCondition("scope",
filterInput.getView_equals()));
+ }
+
+ if (filterInputAsMap.get("and") != null) {
+ final List<Map<String, Object>> andFilterInputAsMap =
(List<Map<String, Object>>) filterInputAsMap.get("and");
+
+ rootSubConditions.add(filtersToCondition(filterInput.getAnd(),
andFilterInputAsMap, this::filterInputCondition, "and"));
+ }
+
+ if (filterInputAsMap.get("or") != null) {
+ final List<Map<String, Object>> orFilterInputAsMap =
(List<Map<String, Object>>) filterInputAsMap.get("or");
+
+ rootSubConditions.add(filtersToCondition(filterInput.getOr(),
orFilterInputAsMap, this::filterInputCondition, "or"));
+ }
+
+ return booleanCondition("and", rootSubConditions);
+ }
+
+}
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/FindTopicsConnectionDataFetcher.java
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/FindTopicsConnectionDataFetcher.java
index 80aad1f..5aa0ab9 100644
---
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/FindTopicsConnectionDataFetcher.java
+++
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/FindTopicsConnectionDataFetcher.java
@@ -17,12 +17,21 @@
package org.apache.unomi.graphql.fetchers;
import graphql.schema.DataFetchingEnvironment;
+import org.apache.unomi.api.PartialList;
+import org.apache.unomi.api.Topic;
+import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.api.query.Query;
+import org.apache.unomi.api.services.TopicService;
+import org.apache.unomi.graphql.condition.factories.TopicConditionFactory;
import org.apache.unomi.graphql.services.ServiceManager;
import org.apache.unomi.graphql.types.input.CDPOrderByInput;
import org.apache.unomi.graphql.types.input.CDPTopicFilterInput;
+import org.apache.unomi.graphql.types.output.CDPPageInfo;
import org.apache.unomi.graphql.types.output.CDPTopicConnection;
+import org.apache.unomi.graphql.types.output.CDPTopicEdge;
import java.util.List;
+import java.util.stream.Collectors;
public class FindTopicsConnectionDataFetcher extends
BaseConnectionDataFetcher<CDPTopicConnection> {
@@ -39,10 +48,22 @@ public class FindTopicsConnectionDataFetcher extends
BaseConnectionDataFetcher<C
@Override
public CDPTopicConnection get(DataFetchingEnvironment environment) throws
Exception {
final ServiceManager serviceManager = environment.getContext();
+
+ final TopicService topicService =
serviceManager.getService(TopicService.class);
+
final ConnectionParams params = parseConnectionParams(environment);
- // Unomi doesn't have an API for that yet, so return a stub
- return null;
+ final Query query = buildQuery(createCondition(environment),
orderByInput, params);
+
+ final PartialList<Topic> topicPartialList = topicService.search(query);
+
+ final List<CDPTopicEdge> edges =
topicPartialList.getList().stream().map(CDPTopicEdge::new).collect(Collectors.toList());
+
+ return new CDPTopicConnection(topicPartialList.getTotalSize(), edges,
new CDPPageInfo());
+ }
+
+ private Condition createCondition(final DataFetchingEnvironment
environment) {
+ return
TopicConditionFactory.get(environment).filterInputCondition(filterInput,
environment.getArgument("filter"));
}
}
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/TopicDataFetcher.java
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/TopicDataFetcher.java
index 05ba1af..2a4a733 100644
---
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/TopicDataFetcher.java
+++
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/fetchers/TopicDataFetcher.java
@@ -18,6 +18,9 @@ package org.apache.unomi.graphql.fetchers;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
+import org.apache.unomi.api.Topic;
+import org.apache.unomi.api.services.TopicService;
+import org.apache.unomi.graphql.services.ServiceManager;
import org.apache.unomi.graphql.types.output.CDPTopic;
public class TopicDataFetcher implements DataFetcher<CDPTopic> {
@@ -30,7 +33,16 @@ public class TopicDataFetcher implements
DataFetcher<CDPTopic> {
@Override
public CDPTopic get(DataFetchingEnvironment environment) throws Exception {
- // Unomi doesn't have an API for that yet, so return a stub
+ final ServiceManager serviceManager = environment.getContext();
+
+ final TopicService topicService =
serviceManager.getService(TopicService.class);
+
+ final Topic topic = topicService.load(topicId);
+
+ if (topic != null) {
+ return new CDPTopic(topic);
+ }
+
return null;
}
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopic.java
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopic.java
index 1dda33d..eb4271c 100644
---
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopic.java
+++
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopic.java
@@ -22,6 +22,7 @@ import graphql.annotations.annotationTypes.GraphQLID;
import graphql.annotations.annotationTypes.GraphQLName;
import graphql.annotations.annotationTypes.GraphQLNonNull;
import graphql.schema.DataFetchingEnvironment;
+import org.apache.unomi.api.Topic;
import static org.apache.unomi.graphql.types.output.CDPTopic.TYPE_NAME;
@@ -31,23 +32,33 @@ public class CDPTopic {
public static final String TYPE_NAME = "CDP_Topic";
+ private final Topic topic;
+
+ public CDPTopic() {
+ this(null);
+ }
+
+ public CDPTopic(Topic topic) {
+ this.topic = topic;
+ }
+
@GraphQLID
@GraphQLField
@GraphQLNonNull
public String id(final DataFetchingEnvironment environment) {
- return null;
+ return topic != null ? topic.getTopicId() : null;
}
@GraphQLField
@GraphQLNonNull
public String name(final DataFetchingEnvironment environment) {
- return null;
+ return topic != null ? topic.getName() : null;
}
@GraphQLField
@GraphQLNonNull
public CDPView view(final DataFetchingEnvironment environment) {
- return null;
+ return topic != null ? new CDPView(topic.getScope()) : null;
}
}
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopicConnection.java
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopicConnection.java
index 8bfec66..897dce0 100644
---
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopicConnection.java
+++
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopicConnection.java
@@ -20,6 +20,7 @@ import graphql.annotations.annotationTypes.GraphQLField;
import graphql.annotations.annotationTypes.GraphQLName;
import graphql.schema.DataFetchingEnvironment;
+import java.util.ArrayList;
import java.util.List;
import static
org.apache.unomi.graphql.types.output.CDPTopicConnection.TYPE_NAME;
@@ -29,19 +30,35 @@ public class CDPTopicConnection {
public static final String TYPE_NAME = "CDP_TopicConnection";
+ private Long totalCount;
+
+ private List<CDPTopicEdge> edges;
+
+ private CDPPageInfo pageInfo;
+
+ public CDPTopicConnection() {
+ this(0L, new ArrayList<>(), new CDPPageInfo());
+ }
+
+ public CDPTopicConnection(final Long totalCount, final List<CDPTopicEdge>
edges, final CDPPageInfo pageInfo) {
+ this.totalCount = totalCount;
+ this.edges = edges;
+ this.pageInfo = pageInfo;
+ }
+
@GraphQLField
- public Integer totalCount(final DataFetchingEnvironment environment) {
- return 0;
+ public Long totalCount(final DataFetchingEnvironment environment) {
+ return totalCount;
}
@GraphQLField
public List<CDPTopicEdge> edges(final DataFetchingEnvironment environment)
{
- return null;
+ return edges;
}
@GraphQLField
public CDPPageInfo pageInfo(final DataFetchingEnvironment environment) {
- return null;
+ return pageInfo;
}
}
diff --git
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopicEdge.java
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopicEdge.java
index 3c75519..91194c0 100644
---
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopicEdge.java
+++
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/types/output/CDPTopicEdge.java
@@ -19,6 +19,7 @@ package org.apache.unomi.graphql.types.output;
import graphql.annotations.annotationTypes.GraphQLField;
import graphql.annotations.annotationTypes.GraphQLName;
import graphql.schema.DataFetchingEnvironment;
+import org.apache.unomi.api.Topic;
import static org.apache.unomi.graphql.types.output.CDPTopicEdge.TYPE_NAME;
@@ -27,14 +28,20 @@ public class CDPTopicEdge {
public static final String TYPE_NAME = "CDP_TopicEdge";
+ private final Topic topic;
+
+ public CDPTopicEdge(final Topic topic) {
+ this.topic = topic;
+ }
+
@GraphQLField
public CDPTopic node(final DataFetchingEnvironment environment) {
- return null;
+ return new CDPTopic(topic);
}
@GraphQLField
public String cursor(final DataFetchingEnvironment environment) {
- return null;
+ return topic.getTopicId();
}
}
diff --git
a/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLTopicIT.java
b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLTopicIT.java
new file mode 100644
index 0000000..482c38f
--- /dev/null
+++ b/itests/src/test/java/org/apache/unomi/itests/graphql/GraphQLTopicIT.java
@@ -0,0 +1,78 @@
+/*
+ * 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.apache.unomi.itests.graphql;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.unomi.api.services.TopicService;
+import org.junit.Assert;
+import org.junit.Test;
+import org.ops4j.pax.exam.util.Filter;
+
+import javax.inject.Inject;
+import java.io.IOException;
+
+public class GraphQLTopicIT extends BaseGraphQLIT {
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected TopicService topicService;
+
+ @Test
+ public void testCRUD() throws IOException {
+ try (CloseableHttpResponse response =
post("graphql/topic/create-topic.json")) {
+ final ResponseContext context =
ResponseContext.parse(response.getEntity());
+
+ Assert.assertEquals("testTopic",
context.getValue("data.cdp.createOrUpdateTopic.id"));
+ }
+
+ try (CloseableHttpResponse response =
post("graphql/topic/update-topic.json")) {
+ final ResponseContext context =
ResponseContext.parse(response.getEntity());
+
+ Assert.assertEquals("testTopic",
context.getValue("data.cdp.createOrUpdateTopic.id"));
+ Assert.assertEquals("testTopicName Updated",
context.getValue("data.cdp.createOrUpdateTopic.name"));
+ Assert.assertEquals("testTopicView",
context.getValue("data.cdp.createOrUpdateTopic.view.name"));
+ }
+
+ try (CloseableHttpResponse response =
post("graphql/topic/get-topic.json")) {
+ final ResponseContext context =
ResponseContext.parse(response.getEntity());
+
+ Assert.assertEquals("testTopic",
context.getValue("data.cdp.getTopic.id"));
+ Assert.assertEquals("testTopicName Updated",
context.getValue("data.cdp.getTopic.name"));
+ Assert.assertEquals("testTopicView",
context.getValue("data.cdp.getTopic.view.name"));
+ }
+
+ try (CloseableHttpResponse response =
post("graphql/topic/find-topics.json")) {
+ final ResponseContext context =
ResponseContext.parse(response.getEntity());
+
+ Assert.assertEquals(1, (int)
context.getValue("data.cdp.findTopics.totalCount"));
+
Assert.assertNotNull(context.getValue("data.cdp.findTopics.edges"));
+ }
+
+ try (CloseableHttpResponse response =
post("graphql/topic/delete-topic.json")) {
+ final ResponseContext context =
ResponseContext.parse(response.getEntity());
+
+ Assert.assertTrue(context.getValue("data.cdp.deleteTopic"));
+ }
+
+ try (CloseableHttpResponse response =
post("graphql/topic/get-topic.json")) {
+ final ResponseContext context =
ResponseContext.parse(response.getEntity());
+
+ Assert.assertNull(context.getValue("data.cdp.getTopic"));
+ }
+ }
+
+}
diff --git a/itests/src/test/resources/graphql/topic/create-topic.json
b/itests/src/test/resources/graphql/topic/create-topic.json
new file mode 100644
index 0000000..59e46cc
--- /dev/null
+++ b/itests/src/test/resources/graphql/topic/create-topic.json
@@ -0,0 +1,11 @@
+{
+ "operationName": "createOrUpdateTopic",
+ "variables": {
+ "topic": {
+ "id": "testTopic",
+ "name": "testTopicName",
+ "view": "testTopicView"
+ }
+ },
+ "query": "mutation createOrUpdateTopic($topic: CDP_TopicInput) {\n cdp {\n
createOrUpdateTopic(topic: $topic) {\n id\n }\n }\n}\n"
+}
diff --git a/itests/src/test/resources/graphql/topic/delete-topic.json
b/itests/src/test/resources/graphql/topic/delete-topic.json
new file mode 100644
index 0000000..8c18f44
--- /dev/null
+++ b/itests/src/test/resources/graphql/topic/delete-topic.json
@@ -0,0 +1,7 @@
+{
+ "operationName": "deleteTopic",
+ "variables": {
+ "topicId": "testTopic"
+ },
+ "query": "mutation deleteTopic($topicId: ID!) {\n cdp {\n
deleteTopic(topicID: $topicId)\n }\n}\n"
+}
diff --git a/itests/src/test/resources/graphql/topic/find-topics.json
b/itests/src/test/resources/graphql/topic/find-topics.json
new file mode 100644
index 0000000..bd4b4c0
--- /dev/null
+++ b/itests/src/test/resources/graphql/topic/find-topics.json
@@ -0,0 +1,25 @@
+{
+ "operationName": "findTopics",
+ "variables": {
+ "filterInput": {
+ "or": [
+ {
+ "id_equals": "testTopic"
+ },
+ {
+ "id_equals": "testTopic2"
+ },
+ {
+ "id_equals": "testTopic3"
+ },
+ {
+ "id_equals": "testTopic4"
+ },
+ {
+ "id_equals": "testTopic5"
+ }
+ ]
+ }
+ },
+ "query": "query findTopics($filterInput: CDP_TopicFilterInput) {\n cdp {\n
findTopics(filter: $filterInput) {\n totalCount\n edges {\n
node {\n id\n }\n }\n }\n }\n}\n"
+}
diff --git a/itests/src/test/resources/graphql/topic/get-topic.json
b/itests/src/test/resources/graphql/topic/get-topic.json
new file mode 100644
index 0000000..0bf4144
--- /dev/null
+++ b/itests/src/test/resources/graphql/topic/get-topic.json
@@ -0,0 +1,7 @@
+{
+ "operationName": "getTopic",
+ "variables": {
+ "topicId": "testTopic"
+ },
+ "query": "query getTopic($topicId: ID!) {\n cdp {\n getTopic(topicID:
$topicId) {\n id\n name\n view {\n name\n }\n }\n
}\n}\n"
+}
diff --git a/itests/src/test/resources/graphql/topic/update-topic.json
b/itests/src/test/resources/graphql/topic/update-topic.json
new file mode 100644
index 0000000..8efd5e6
--- /dev/null
+++ b/itests/src/test/resources/graphql/topic/update-topic.json
@@ -0,0 +1,11 @@
+{
+ "operationName": "createOrUpdateTopic",
+ "variables": {
+ "topic": {
+ "id": "testTopic",
+ "name": "testTopicName Updated",
+ "view": "testTopicView"
+ }
+ },
+ "query": "mutation createOrUpdateTopic($topic: CDP_TopicInput) {\n cdp {\n
createOrUpdateTopic(topic: $topic) {\n id\n name\n view {\n
name\n }\n }\n }\n}\n"
+}
diff --git
a/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/topic.json
b/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/topic.json
new file mode 100644
index 0000000..d2d90cb
--- /dev/null
+++
b/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/topic.json
@@ -0,0 +1,20 @@
+{
+ "dynamic_templates": [
+ {
+ "all": {
+ "match": "*",
+ "match_mapping_type": "string",
+ "mapping": {
+ "type": "text",
+ "analyzer": "folding",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ }
+ }
+ }
+ ]
+}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/CustomObjectMapper.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/CustomObjectMapper.java
index a641e2b..c2af0d5 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/CustomObjectMapper.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/CustomObjectMapper.java
@@ -77,6 +77,7 @@ public class CustomObjectMapper extends ObjectMapper {
classes.put(Session.ITEM_TYPE, Session.class);
classes.put(ConditionType.ITEM_TYPE, ConditionType.class);
classes.put(ActionType.ITEM_TYPE, ActionType.class);
+ classes.put(Topic.ITEM_TYPE, Topic.class);
for (Map.Entry<String, Class<? extends Item>> entry :
classes.entrySet()) {
propertyTypedObjectDeserializer.registerMapping("itemType=" +
entry.getKey(), entry.getValue());
itemDeserializer.registerMapping(entry.getKey(), entry.getValue());
diff --git
a/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/topicPropertyCondition.json
b/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/topicPropertyCondition.json
new file mode 100644
index 0000000..f6ee28d
--- /dev/null
+++
b/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/topicPropertyCondition.json
@@ -0,0 +1,31 @@
+{
+ "metadata": {
+ "id": "topicPropertyCondition",
+ "name": "topicPropertyCondition",
+ "description": "",
+ "systemTags": [
+ "condition",
+ "topicCondition"
+ ],
+ "readOnly": true
+ },
+ "conditionEvaluator": "propertyConditionEvaluator",
+ "queryBuilder": "propertyConditionESQueryBuilder",
+ "parameters": [
+ {
+ "id": "propertyName",
+ "type": "string",
+ "multivalued": false
+ },
+ {
+ "id": "comparisonOperator",
+ "type": "comparisonOperator",
+ "multivalued": false
+ },
+ {
+ "id": "propertyValue",
+ "type": "string",
+ "multivalued": false
+ }
+ ]
+}
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/topics/TopicServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/topics/TopicServiceImpl.java
new file mode 100644
index 0000000..7058447
--- /dev/null
+++
b/services/src/main/java/org/apache/unomi/services/impl/topics/TopicServiceImpl.java
@@ -0,0 +1,81 @@
+/*
+ * 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.apache.unomi.services.impl.topics;
+
+import org.apache.unomi.api.PartialList;
+import org.apache.unomi.api.Topic;
+import org.apache.unomi.api.query.Query;
+import org.apache.unomi.api.services.TopicService;
+import org.apache.unomi.persistence.spi.PersistenceService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+public class TopicServiceImpl implements TopicService,
SynchronousBundleListener {
+
+ private PersistenceService persistenceService;
+
+ private BundleContext bundleContext;
+
+ public void setPersistenceService(PersistenceService persistenceService) {
+ this.persistenceService = persistenceService;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ public Topic load(final String topicId) {
+ return persistenceService.load(topicId, Topic.class);
+ }
+
+ @Override
+ public Topic save(final Topic topic) {
+ if (persistenceService.save(topic)) {
+ persistenceService.refreshIndex(Topic.class, null);
+
+ return topic;
+ }
+
+ return null;
+ }
+
+ @Override
+ public PartialList<Topic> search(final Query query) {
+ return persistenceService.query(query.getCondition(),
query.getSortby(), Topic.class, query.getOffset(), query.getLimit());
+ }
+
+ @Override
+ public boolean delete(String topicId) {
+ return persistenceService.remove(topicId, Topic.class);
+ }
+
+ @Override
+ public void bundleChanged(BundleEvent bundleEvent) {
+ // do nothing
+ }
+
+ public void postConstruct() {
+ bundleContext.addBundleListener(this);
+ }
+
+ public void preDestroy() {
+ bundleContext.removeBundleListener(this);
+ }
+
+}
diff --git a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 68edc8a..1a5876e 100644
--- a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -262,6 +262,18 @@
</interfaces>
</service>
+ <bean id="topicServiceImpl"
class="org.apache.unomi.services.impl.topics.TopicServiceImpl"
+ init-method="postConstruct" destroy-method="preDestroy">
+ <property name="persistenceService" ref="persistenceService"/>
+ <property name="bundleContext" ref="blueprintBundleContext"/>
+ </bean>
+ <service id="topicService" ref="topicServiceImpl">
+ <interfaces>
+ <value>org.apache.unomi.api.services.TopicService</value>
+ <value>org.osgi.framework.SynchronousBundleListener</value>
+ </interfaces>
+ </service>
+
<!-- We use a listener here because using the list directly for listening
to proxies coming from the same bundle didn't seem to work -->
<reference-list id="eventListenerServices"