You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2019/09/26 05:18:05 UTC

[unomi] 01/01: UNOMI-249 New shell commands to make plugin development easier The following shell dev commands have been added: - List events - Search for events by profile Id and type - Remove a profile - Remove a rule - Remove a segment - View a segment - Undeploy definitions provided by a plugin - Added the possibility to deploy all the definitions contained in a plugin with the deploy-definition command.

This is an automated email from the ASF dual-hosted git repository.

shuber pushed a commit to branch UNOMI-249-new-shell-commands
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit 2f877b51b5bd3398bbbbf473c2a698054de05f11
Author: Serge Huber <sh...@apache.org>
AuthorDate: Thu Sep 26 07:17:54 2019 +0200

    UNOMI-249 New shell commands to make plugin development easier
    The following shell dev commands have been added:
    - List events
    - Search for events by profile Id and type
    - Remove a profile
    - Remove a rule
    - Remove a segment
    - View a segment
    - Undeploy definitions provided by a plugin
    - Added the possibility to deploy all the definitions contained in a plugin with the deploy-definition command.
    
    Signed-off-by: Serge Huber <sh...@apache.org>
---
 .../unomi/shell/commands/DeployDefinition.java     | 245 +++------------------
 .../shell/commands/DeploymentCommandSupport.java   | 232 +++++++++++++++++++
 .../org/apache/unomi/shell/commands/EventList.java |  78 +++++++
 .../apache/unomi/shell/commands/EventSearch.java   |  99 +++++++++
 .../{SegmentView.java => ProfileRemove.java}       |  22 +-
 .../commands/{SegmentView.java => RuleRemove.java} |  22 +-
 .../{SegmentView.java => SegmentRemove.java}       |  28 ++-
 .../apache/unomi/shell/commands/SegmentView.java   |   2 +-
 .../unomi/shell/commands/UndeployDefinition.java   | 111 ++++++++++
 9 files changed, 586 insertions(+), 253 deletions(-)

diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeployDefinition.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeployDefinition.java
index d0baae9..9365de8 100644
--- a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeployDefinition.java
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeployDefinition.java
@@ -16,13 +16,8 @@
  */
 package org.apache.unomi.shell.commands;
 
-import org.apache.commons.lang3.StringUtils;
-import org.apache.karaf.shell.api.action.Action;
-import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.apache.karaf.shell.api.console.Session;
 import org.apache.unomi.api.Patch;
 import org.apache.unomi.api.PersonaWithSessions;
 import org.apache.unomi.api.PropertyType;
@@ -33,260 +28,86 @@ import org.apache.unomi.api.goals.Goal;
 import org.apache.unomi.api.rules.Rule;
 import org.apache.unomi.api.segments.Scoring;
 import org.apache.unomi.api.segments.Segment;
-import org.apache.unomi.api.services.*;
 import org.apache.unomi.persistence.spi.CustomObjectMapper;
-import org.jline.reader.LineReader;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
 
 import java.io.IOException;
 import java.net.URL;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
 
-@Command(scope = "unomi", name = "deploy-definition", description = "This will deploy a specific definition")
+@Command(scope = "unomi", name = "deploy-definition", description = "This will deploy Unomi definitions contained in bundles")
 @Service
-public class DeployDefinition implements Action {
+public class DeployDefinition extends DeploymentCommandSupport {
 
-    @Reference
-    DefinitionsService definitionsService;
-
-    @Reference
-    GoalsService goalsService;
-
-    @Reference
-    ProfileService profileService;
-
-    @Reference
-    RulesService rulesService;
-
-    @Reference
-    SegmentService segmentService;
-
-    @Reference
-    PatchService patchService;
-
-    @Reference
-    BundleContext bundleContext;
-
-    @Reference
-    Session session;
-
-    private final static List<String> definitionTypes = Arrays.asList("condition", "action", "goal", "campaign", "persona", "property", "rule", "segment", "scoring", "patch");
-
-    @Argument(index = 0, name = "bundleId", description = "The bundle identifier where to find the definition", multiValued = false)
-    Long bundleIdentifier;
-
-    @Argument(index = 1, name = "type", description = "The kind of definitions you want to load (e.g.: condition, action, ..)", required = false, multiValued = false)
-    String definitionType;
-
-    @Argument(index = 2, name = "fileName", description = "The name of the file which contains the definition, without its extension (e.g: firstName)", required = false, multiValued = false)
-    String fileName;
-
-    public Object execute() throws Exception {
-        List<Bundle> bundlesToUpdate;
-        if (bundleIdentifier == null) {
-            List<Bundle> bundles = new ArrayList<>();
-            for (Bundle bundle : bundleContext.getBundles()) {
-                if (bundle.findEntries("META-INF/cxs/", "*.json", true) != null) {
-                    bundles.add(bundle);
+    public void processDefinition(String definitionType, URL definitionURL) {
+        try {
+            if (ALL_OPTION_LABEL.equals(definitionType)) {
+                String definitionURLString = definitionURL.toString();
+                for (String possibleDefinitionType : definitionTypes) {
+                    if (definitionURLString.contains(getDefinitionTypePath(possibleDefinitionType))) {
+                        definitionType = possibleDefinitionType;
+                        break;
+                    }
+                }
+                if (ALL_OPTION_LABEL.equals(definitionType)) {
+                    System.out.println("Couldn't resolve definition type for definition URL " + definitionURL);
+                    return;
                 }
             }
-
-            bundles = bundles.stream()
-                    .filter(b -> definitionTypes.stream().anyMatch((t) -> b.findEntries(getDefinitionTypePath(t), "*.json", true) != null))
-                    .collect(Collectors.toList());
-
-            List<String> stringList = bundles.stream().map(Bundle::getSymbolicName).collect(Collectors.toList());
-            stringList.add(0, "* (All)");
-
-            String bundleAnswer = askUserWithAuthorizedAnswer(session, "Which bundle ?" + getValuesWithNumber(stringList) + "\n",
-                    IntStream.range(1,bundles.size()+1).mapToObj(Integer::toString).collect(Collectors.toList()));
-            if (bundleAnswer.equals("1")) {
-                bundlesToUpdate = bundles;
-            } else {
-                bundlesToUpdate = Collections.singletonList(bundles.get(new Integer(bundleAnswer) - 2));
-            }
-        } else {
-            Bundle bundle = bundleContext.getBundle(bundleIdentifier);
-
-            if (bundle == null) {
-                System.out.println("Couldn't find a bundle with id: " + bundleIdentifier);
-                return null;
-            }
-
-            bundlesToUpdate = Collections.singletonList(bundle);
-        }
-
-        if (definitionType == null) {
-            List<String> values = definitionTypes.stream().filter((t) -> bundlesToUpdate.stream().anyMatch(b->b.findEntries(getDefinitionTypePath(t), "*.json", true) != null)).collect(Collectors.toList());
-
-            if (values.isEmpty()) {
-                System.out.println("Couldn't find definitions in bundle : " + bundlesToUpdate);
-                return null;
-            }
-
-            String definitionTypeAnswer = askUserWithAuthorizedAnswer(session, "Which kind of definition do you want to load?" + getValuesWithNumber(values) + "\n",
-                    IntStream.range(1,values.size()+1).mapToObj(Integer::toString).collect(Collectors.toList()));
-            definitionType = values.get(new Integer(definitionTypeAnswer)-1);
-        }
-
-        if (!definitionTypes.contains(definitionType)) {
-            System.out.println("Invalid type '" + definitionType + "' , allowed values : " +definitionTypes);
-            return null;
-        }
-
-        String path = getDefinitionTypePath(definitionType);
-        List<URL> values = bundlesToUpdate.stream().flatMap(b->b.findEntries(path, "*.json", true) != null ? Collections.list(b.findEntries(path, "*.json", true)).stream() : Stream.empty()).collect(Collectors.toList());
-        if (values.isEmpty()) {
-            System.out.println("Couldn't find definitions in bundle with id: " + bundleIdentifier + " and definition path: " + path);
-            return null;
-        }
-
-        if (fileName == null) {
-            List<String> stringList = values.stream().map(u -> StringUtils.substringAfterLast(u.getFile(), "/")).sorted().collect(Collectors.toList());
-            stringList.add(0, "* (All)");
-            String fileNameAnswer = askUserWithAuthorizedAnswer(session, "Which file do you want to load ?" + getValuesWithNumber(stringList) + "\n",
-                    IntStream.range(1,stringList.size()+1).mapToObj(Integer::toString).collect(Collectors.toList()));
-            fileName = stringList.get(new Integer(fileNameAnswer)-1);
-        }
-        if (fileName.startsWith("*")) {
-            for (URL url : values) {
-                updateDefinition(definitionType, url);
-            }
-        } else {
-            if (!fileName.contains("/")) {
-                fileName = "/" + fileName;
-            }
-            if (!fileName.endsWith(".json")) {
-                fileName += ".json";
-            }
-
-            Optional<URL> optionalURL = values.stream().filter(u -> u.getFile().endsWith(fileName)).findFirst();
-            if (optionalURL.isPresent()) {
-                URL url = optionalURL.get();
-                updateDefinition(definitionType, url);
-            } else {
-                System.out.println("Couldn't find file " + fileName);
-                return null;
-            }
-        }
-
-        return null;
-    }
-
-    private String askUserWithAuthorizedAnswer(Session session, String msg, List<String> authorizedAnswer) throws IOException {
-        String answer;
-        do {
-            answer = promptMessageToUser(session,msg);
-        } while (!authorizedAnswer.contains(answer.toLowerCase()));
-        return answer;
-    }
-
-    private String promptMessageToUser(Session session, String msg) throws IOException {
-        LineReader reader = (LineReader) session.get(".jline.reader");
-        return reader.readLine(msg, null);
-    }
-
-    private String getValuesWithNumber(List<String> values) {
-        StringBuilder definitionTypesWithNumber = new StringBuilder();
-        for (int i = 0; i < values.size(); i++) {
-            definitionTypesWithNumber.append("\n").append(i+1).append(". ").append(values.get(i));
-        }
-        return definitionTypesWithNumber.toString();
-    }
-
-    private void updateDefinition(String definitionType, URL definitionURL) {
-        try {
-                    
+            boolean successful = true;
             switch (definitionType) {
-                case "condition":
+                case CONDITION_DEFINITION_TYPE:
                     ConditionType conditionType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, ConditionType.class);
                     definitionsService.setConditionType(conditionType);
                     break;
-                case "action":
+                case ACTION_DEFINITION_TYPE:
                     ActionType actionType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, ActionType.class);
                     definitionsService.setActionType(actionType);
                     break;
-                case "goal":
+                case GOAL_DEFINITION_TYPE:
                     Goal goal = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Goal.class);
                     goalsService.setGoal(goal);
                     break;
-                case "campaign":
+                case CAMPAIGN_DEFINITION_TYPE:
                     Campaign campaign = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Campaign.class);
                     goalsService.setCampaign(campaign);
                     break;
-                case "persona":
+                case PERSONA_DEFINITION_TYPE:
                     PersonaWithSessions persona = CustomObjectMapper.getObjectMapper().readValue(definitionURL, PersonaWithSessions.class);
                     profileService.savePersonaWithSessions(persona);
                     break;
-                case "property":
+                case PROPERTY_DEFINITION_TYPE:
                     PropertyType propertyType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, PropertyType.class);
                     profileService.setPropertyTypeTarget(definitionURL, propertyType);
                     profileService.setPropertyType(propertyType);
                     break;
-                case "rule":
+                case RULE_DEFINITION_TYPE:
                     Rule rule = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Rule.class);
                     rulesService.setRule(rule);
                     break;
-                case "segment":
+                case SEGMENT_DEFINITION_TYPE:
                     Segment segment = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Segment.class);
                     segmentService.setSegmentDefinition(segment);
                     break;
-                case "scoring":
+                case SCORING_DEFINITION_TYPE:
                     Scoring scoring = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Scoring.class);
                     segmentService.setScoringDefinition(scoring);
                     break;
-                case "patch":
+                case PATCH_DEFINITION_TYPE:
                     Patch patch = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Patch.class);
                     patchService.patch(patch);
                     break;
+                default:
+                    System.out.println("Unrecognized definition type:" + definitionType);
+                    successful = false;
+                    break;
+            }
+            if (successful) {
+                System.out.println("Predefined definition registered : " + definitionURL.getFile());
             }
-            System.out.println("Predefined definition registered : "+definitionURL.getFile());
         } catch (IOException e) {
             System.out.println("Error while saving definition " + definitionURL);
             System.out.println(e.getMessage());
         }
     }
 
-    private String getDefinitionTypePath(String definitionType) {
-        StringBuilder path = new StringBuilder("META-INF/cxs/");
-        switch (definitionType) {
-            case "condition":
-                path.append("conditions");
-                break;
-            case "action":
-                path.append("actions");
-                break;
-            case "goal":
-                path.append("goals");
-                break;
-            case "campaign":
-                path.append("campaigns");
-                break;
-            case "persona":
-                path.append("personas");
-                break;
-            case "property":
-                path.append("properties");
-                break;
-            case "rule":
-                path.append("rules");
-                break;
-            case "segment":
-                path.append("segments");
-                break;
-            case "scoring":
-                path.append("scoring");
-                break;
-            case "patch":
-                path.append("patches");
-                break;
-        }
-
-        return path.toString();
-    }
 
 }
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeploymentCommandSupport.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeploymentCommandSupport.java
new file mode 100644
index 0000000..e417d0d
--- /dev/null
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/DeploymentCommandSupport.java
@@ -0,0 +1,232 @@
+/*
+ * 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.shell.commands;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.unomi.api.services.*;
+import org.jline.reader.LineReader;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+public abstract class DeploymentCommandSupport implements Action {
+
+    public static final String ALL_OPTION_LABEL = "* (All)";
+    @Reference
+    DefinitionsService definitionsService;
+
+    @Reference
+    GoalsService goalsService;
+
+    @Reference
+    ProfileService profileService;
+
+    @Reference
+    RulesService rulesService;
+
+    @Reference
+    SegmentService segmentService;
+
+    @Reference
+    PatchService patchService;
+
+    @Reference
+    BundleContext bundleContext;
+
+    @Reference
+    Session session;
+
+    public static final String CONDITION_DEFINITION_TYPE = "conditions";
+    public static final String ACTION_DEFINITION_TYPE = "actions";
+    public static final String GOAL_DEFINITION_TYPE = "goals";
+    public static final String CAMPAIGN_DEFINITION_TYPE = "campaigns";
+    public static final String PERSONA_DEFINITION_TYPE = "personas";
+    public static final String PROPERTY_DEFINITION_TYPE = "properties";
+    public static final String RULE_DEFINITION_TYPE = "rules";
+    public static final String SEGMENT_DEFINITION_TYPE = "segments";
+    public static final String SCORING_DEFINITION_TYPE = "scoring";
+    public static final String PATCH_DEFINITION_TYPE = "patches";
+    public static final String VALUE_DEFINITION_TYPE = "values";
+    public static final String MERGER_DEFINITION_TYPE = "mergers";
+    public static final String MAPPING_DEFINITION_TYPE = "mappings";
+
+    protected final static List<String> definitionTypes = Arrays.asList(
+            CONDITION_DEFINITION_TYPE,
+            ACTION_DEFINITION_TYPE,
+            GOAL_DEFINITION_TYPE,
+            CAMPAIGN_DEFINITION_TYPE,
+            PERSONA_DEFINITION_TYPE,
+            PROPERTY_DEFINITION_TYPE,
+            RULE_DEFINITION_TYPE,
+            SEGMENT_DEFINITION_TYPE,
+            SCORING_DEFINITION_TYPE,
+            PATCH_DEFINITION_TYPE,
+            VALUE_DEFINITION_TYPE,
+            MERGER_DEFINITION_TYPE,
+            MAPPING_DEFINITION_TYPE);
+
+    @Argument(index = 0, name = "bundleId", description = "The bundle identifier where to find the definition", multiValued = false)
+    Long bundleIdentifier;
+
+    @Argument(index = 1, name = "type", description = "The kind of definitions you want to load (e.g.: *, conditions, actions, ..)", required = false, multiValued = false)
+    String definitionType;
+
+    @Argument(index = 2, name = "fileName", description = "The name of the file which contains the definition, without its extension (e.g: firstName)", required = false, multiValued = false)
+    String fileName;
+
+    public abstract void processDefinition(String definitionType, URL definitionURL);
+
+    public Object execute() throws Exception {
+        List<Bundle> bundlesToUpdate;
+        if ("*".equals(definitionType)) {
+            definitionType = ALL_OPTION_LABEL;
+        }
+        if ("*".equals(fileName)) {
+            fileName = ALL_OPTION_LABEL;
+        }
+        if (bundleIdentifier == null) {
+            List<Bundle> bundles = new ArrayList<>();
+            for (Bundle bundle : bundleContext.getBundles()) {
+                if (bundle.findEntries("META-INF/cxs/", "*.json", true) != null) {
+                    bundles.add(bundle);
+                }
+            }
+
+            bundles = bundles.stream()
+                    .filter(b -> definitionTypes.stream().anyMatch((t) -> b.findEntries(getDefinitionTypePath(t), "*.json", true) != null))
+                    .collect(Collectors.toList());
+
+            List<String> bundleSymbolicNames = bundles.stream().map(Bundle::getSymbolicName).collect(Collectors.toList());
+            bundleSymbolicNames.add(ALL_OPTION_LABEL);
+
+            String bundleAnswer = askUserWithAuthorizedAnswer(session, "Which bundle ?" + getValuesWithNumber(bundleSymbolicNames) + "\n",
+                    IntStream.range(1,bundleSymbolicNames.size()+1).mapToObj(Integer::toString).collect(Collectors.toList()));
+            String selectedBundle = bundleSymbolicNames.get(new Integer(bundleAnswer)-1);
+            if (selectedBundle.equals(ALL_OPTION_LABEL)) {
+                bundlesToUpdate = bundles;
+            } else {
+                bundlesToUpdate = Collections.singletonList(bundles.get(new Integer(bundleAnswer) - 1));
+            }
+        } else {
+            Bundle bundle = bundleContext.getBundle(bundleIdentifier);
+
+            if (bundle == null) {
+                System.out.println("Couldn't find a bundle with id: " + bundleIdentifier);
+                return null;
+            }
+
+            bundlesToUpdate = Collections.singletonList(bundle);
+        }
+
+        if (definitionType == null) {
+            List<String> possibleDefinitionNames = definitionTypes.stream().filter((t) -> bundlesToUpdate.stream().anyMatch(b->b.findEntries(getDefinitionTypePath(t), "*.json", true) != null)).collect(Collectors.toList());
+            possibleDefinitionNames.add(ALL_OPTION_LABEL);
+
+            if (possibleDefinitionNames.isEmpty()) {
+                System.out.println("Couldn't find definitions in bundle : " + bundlesToUpdate);
+                return null;
+            }
+
+            String definitionTypeAnswer = askUserWithAuthorizedAnswer(session, "Which kind of definition do you want to load?" + getValuesWithNumber(possibleDefinitionNames) + "\n",
+                    IntStream.range(1,possibleDefinitionNames.size()+1).mapToObj(Integer::toString).collect(Collectors.toList()));
+            definitionType = possibleDefinitionNames.get(new Integer(definitionTypeAnswer)-1);
+        }
+
+        if (!definitionTypes.contains(definitionType) && !ALL_OPTION_LABEL.equals(definitionType)) {
+            System.out.println("Invalid type '" + definitionType + "' , allowed values : " +definitionTypes);
+            return null;
+        }
+
+        String definitionTypePath = getDefinitionTypePath(definitionType);
+        List<URL> definitionTypeURLs = bundlesToUpdate.stream().flatMap(b->b.findEntries(definitionTypePath, "*.json", true) != null ? Collections.list(b.findEntries(definitionTypePath, "*.json", true)).stream() : Stream.empty()).collect(Collectors.toList());
+        if (definitionTypeURLs.isEmpty()) {
+            System.out.println("Couldn't find definitions in bundle with id: " + bundleIdentifier + " and definition path: " + definitionTypePath);
+            return null;
+        }
+
+        if (fileName == null) {
+            List<String> definitionTypeFileNames = definitionTypeURLs.stream().map(u -> StringUtils.substringAfterLast(u.getFile(), "/")).sorted().collect(Collectors.toList());
+            definitionTypeFileNames.add(ALL_OPTION_LABEL);
+            String fileNameAnswer = askUserWithAuthorizedAnswer(session, "Which file do you want to load ?" + getValuesWithNumber(definitionTypeFileNames) + "\n",
+                    IntStream.range(1,definitionTypeFileNames.size()+1).mapToObj(Integer::toString).collect(Collectors.toList()));
+            fileName = definitionTypeFileNames.get(new Integer(fileNameAnswer)-1);
+        }
+        if (ALL_OPTION_LABEL.equals(fileName)) {
+            for (URL url : definitionTypeURLs) {
+                processDefinition(definitionType, url);
+            }
+        } else {
+            if (!fileName.contains("/")) {
+                fileName = "/" + fileName;
+            }
+            if (!fileName.endsWith(".json")) {
+                fileName += ".json";
+            }
+
+            Optional<URL> optionalURL = definitionTypeURLs.stream().filter(u -> u.getFile().endsWith(fileName)).findFirst();
+            if (optionalURL.isPresent()) {
+                URL url = optionalURL.get();
+                processDefinition(definitionType, url);
+            } else {
+                System.out.println("Couldn't find file " + fileName);
+                return null;
+            }
+        }
+
+        return null;
+    }
+
+    protected String askUserWithAuthorizedAnswer(Session session, String msg, List<String> authorizedAnswer) throws IOException {
+        String answer;
+        do {
+            answer = promptMessageToUser(session,msg);
+        } while (!authorizedAnswer.contains(answer.toLowerCase()));
+        return answer;
+    }
+
+    protected String promptMessageToUser(Session session, String msg) throws IOException {
+        LineReader reader = (LineReader) session.get(".jline.reader");
+        return reader.readLine(msg, null);
+    }
+
+    protected String getValuesWithNumber(List<String> values) {
+        StringBuilder definitionTypesWithNumber = new StringBuilder();
+        for (int i = 0; i < values.size(); i++) {
+            definitionTypesWithNumber.append("\n").append(i+1).append(". ").append(values.get(i));
+        }
+        return definitionTypesWithNumber.toString();
+    }
+
+    protected String getDefinitionTypePath(String definitionType) {
+        StringBuilder path = new StringBuilder("META-INF/cxs/");
+        if (!ALL_OPTION_LABEL.equals(definitionType)) {
+            path.append(definitionType);
+        }
+        return path.toString();
+    }
+
+}
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/EventList.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/EventList.java
new file mode 100644
index 0000000..9991b47
--- /dev/null
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/EventList.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.shell.commands;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.unomi.api.Event;
+import org.apache.unomi.api.PartialList;
+import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.api.services.DefinitionsService;
+import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.common.DataTable;
+
+import java.util.ArrayList;
+
+@Command(scope = "unomi", name = "event-list", description = "This commands lists the latest events updated in the Apache Unomi Context Server")
+@Service
+public class EventList extends ListCommandSupport {
+
+    @Reference
+    private EventService eventService;
+
+    @Reference
+    DefinitionsService definitionsService;
+
+    @Argument(index = 0, name = "maxEntries", description = "The maximum number of entries to retrieve (defaults to 100)", required = false, multiValued = false)
+    int maxEntries = 100;
+
+    String[] columnHeaders = new String[] {
+            "ID",
+            "Type",
+            "Session",
+            "Profile",
+            "Timestamp",
+            "Scope",
+            "Persistent"
+    };
+
+    @Override
+    protected String[] getHeaders() {
+        return columnHeaders;
+    }
+
+    @Override
+    protected DataTable buildDataTable() {
+        Condition matchAllCondition = new Condition(definitionsService.getConditionType("matchAllCondition"));
+        PartialList<Event> lastEvents = eventService.searchEvents(matchAllCondition, 0, maxEntries);
+        DataTable dataTable = new DataTable();
+        for (Event event : lastEvents.getList()) {
+            ArrayList<Comparable> rowData = new ArrayList<>();
+            rowData.add(event.getItemId());
+            rowData.add(event.getEventType());
+            rowData.add(event.getSessionId());
+            rowData.add(event.getProfileId());
+            rowData.add(event.getTimeStamp().toString());
+            rowData.add(event.getScope());
+            rowData.add(Boolean.toString(event.isPersistent()));
+            dataTable.addRow(rowData.toArray(new Comparable[rowData.size()]));
+        }
+        return dataTable;
+    }
+}
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/EventSearch.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/EventSearch.java
new file mode 100644
index 0000000..082f1d3
--- /dev/null
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/EventSearch.java
@@ -0,0 +1,99 @@
+/*
+ * 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.shell.commands;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.unomi.api.Event;
+import org.apache.unomi.api.PartialList;
+import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.api.services.DefinitionsService;
+import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.common.DataTable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Command(scope = "unomi", name = "event-search", description = "This commands search for profile events of a certain type by last timestamp in the Apache Unomi Context Server")
+@Service
+public class EventSearch extends ListCommandSupport  {
+    @Reference
+    private EventService eventService;
+
+    @Reference
+    DefinitionsService definitionsService;
+
+    @Argument(index = 0, name = "profile", description = "The identifier for the profile", required = true, multiValued = false)
+    String profileIdentifier;
+
+    @Argument(index = 1, name = "eventType", description = "The type of the event", required = false, multiValued = false)
+    String eventTypeId;
+
+    @Argument(index = 2, name = "maxEntries", description = "The maximum number of entries to retrieve (defaults to 100)", required = false, multiValued = false)
+    int maxEntries = 100;
+
+    String[] columnHeaders = new String[] {
+            "ID",
+            "Type",
+            "Session",
+            "Profile",
+            "Timestamp",
+            "Scope",
+            "Persistent"
+    };
+
+    @Override
+    protected String[] getHeaders() {
+        return columnHeaders;
+    }
+
+    @Override
+    protected DataTable buildDataTable() {
+        Condition booleanCondition = new Condition(definitionsService.getConditionType("booleanCondition"));
+        booleanCondition.setParameter("operator", "and");
+        List<Condition> subConditions = new ArrayList<>();
+        if (profileIdentifier != null) {
+            Condition eventProfileIdCondition = new Condition(definitionsService.getConditionType("eventPropertyCondition"));
+            eventProfileIdCondition.setParameter("propertyName", "profileId");
+            eventProfileIdCondition.setParameter("comparisonOperator", "equals");
+            eventProfileIdCondition.setParameter("propertyValue", profileIdentifier);
+            subConditions.add(eventProfileIdCondition);
+        }
+        if (eventTypeId != null) {
+            Condition eventTypeIdCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
+            eventTypeIdCondition.setParameter("eventTypeId", eventTypeId);
+            subConditions.add(eventTypeIdCondition);
+        }
+        booleanCondition.setParameter("subConditions", subConditions);
+        PartialList<Event> lastEvents = eventService.searchEvents(booleanCondition, 0, maxEntries);
+        DataTable dataTable = new DataTable();
+        for (Event event : lastEvents.getList()) {
+            ArrayList<Comparable> rowData = new ArrayList<>();
+            rowData.add(event.getItemId());
+            rowData.add(event.getEventType());
+            rowData.add(event.getSessionId());
+            rowData.add(event.getProfileId());
+            rowData.add(event.getTimeStamp().toString());
+            rowData.add(event.getScope());
+            rowData.add(Boolean.toString(event.isPersistent()));
+            dataTable.addRow(rowData.toArray(new Comparable[rowData.size()]));
+        }
+        return dataTable;
+    }
+}
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/ProfileRemove.java
similarity index 56%
copy from tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
copy to tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/ProfileRemove.java
index d502148..6fba5d3 100644
--- a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/ProfileRemove.java
@@ -21,28 +21,20 @@ import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.apache.unomi.api.segments.Segment;
-import org.apache.unomi.api.services.SegmentService;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
+import org.apache.unomi.api.services.ProfileService;
 
-@Command(scope = "unomi", name = "segment-view", description = "This will allows to view a segment in the Apache Unomi Context Server")
+@Command(scope = "unomi", name = "profile-remove", description = "This command will remove a profile")
 @Service
-public class SegmentView implements Action {
+public class ProfileRemove implements Action {
 
     @Reference
-    SegmentService segmentService;
+    ProfileService profileService;
 
-    @Argument(index = 0, name = "segmentId", description = "The identifier for the segment", required = true, multiValued = false)
-    String segmentIdentifier;
+    @Argument(index = 0, name = "profile", description = "The identifier for the profile", required = true, multiValued = false)
+    String profileIdentifier;
 
     public Object execute() throws Exception {
-        Segment segment = segmentService.getSegmentDefinition(segmentIdentifier);
-        if (segment == null) {
-            System.out.println("Couldn't find an action with id=" + segmentIdentifier);
-            return null;
-        }
-        String jsonRule = CustomObjectMapper.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(segment);
-        System.out.println(jsonRule);
+        profileService.delete(profileIdentifier, false);
         return null;
     }
 }
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/RuleRemove.java
similarity index 56%
copy from tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
copy to tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/RuleRemove.java
index d502148..b5afea0 100644
--- a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/RuleRemove.java
@@ -21,28 +21,20 @@ import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.apache.unomi.api.segments.Segment;
-import org.apache.unomi.api.services.SegmentService;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
+import org.apache.unomi.api.services.RulesService;
 
-@Command(scope = "unomi", name = "segment-view", description = "This will allows to view a segment in the Apache Unomi Context Server")
+@Command(scope = "unomi", name = "rule-remove", description = "This will allows to remove a rule in the Apache Unomi Context Server")
 @Service
-public class SegmentView implements Action {
+public class RuleRemove implements Action {
 
     @Reference
-    SegmentService segmentService;
+    RulesService rulesService;
 
-    @Argument(index = 0, name = "segmentId", description = "The identifier for the segment", required = true, multiValued = false)
-    String segmentIdentifier;
+    @Argument(index = 0, name = "rule", description = "The identifier for the rule", required = true, multiValued = false)
+    String ruleIdentifier;
 
     public Object execute() throws Exception {
-        Segment segment = segmentService.getSegmentDefinition(segmentIdentifier);
-        if (segment == null) {
-            System.out.println("Couldn't find an action with id=" + segmentIdentifier);
-            return null;
-        }
-        String jsonRule = CustomObjectMapper.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(segment);
-        System.out.println(jsonRule);
+        rulesService.removeRule(ruleIdentifier);
         return null;
     }
 }
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentRemove.java
similarity index 55%
copy from tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
copy to tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentRemove.java
index d502148..14b4efd 100644
--- a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentRemove.java
@@ -21,13 +21,12 @@ import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.apache.unomi.api.segments.Segment;
+import org.apache.unomi.api.segments.DependentMetadata;
 import org.apache.unomi.api.services.SegmentService;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
 
-@Command(scope = "unomi", name = "segment-view", description = "This will allows to view a segment in the Apache Unomi Context Server")
+@Command(scope = "unomi", name = "segment-remove", description = "Remove segments in the Apache Unomi Context Server")
 @Service
-public class SegmentView implements Action {
+public class SegmentRemove implements Action {
 
     @Reference
     SegmentService segmentService;
@@ -35,14 +34,23 @@ public class SegmentView implements Action {
     @Argument(index = 0, name = "segmentId", description = "The identifier for the segment", required = true, multiValued = false)
     String segmentIdentifier;
 
+    @Argument(index = 1, name = "validate", description = "Check if the segment is used in goals or other segments", required = false, multiValued = false)
+    Boolean validate = true;
+
+
     public Object execute() throws Exception {
-        Segment segment = segmentService.getSegmentDefinition(segmentIdentifier);
-        if (segment == null) {
-            System.out.println("Couldn't find an action with id=" + segmentIdentifier);
-            return null;
+        DependentMetadata dependantMetadata = segmentService.removeSegmentDefinition(segmentIdentifier, validate);
+        if (!validate || (dependantMetadata.getSegments().isEmpty() && dependantMetadata.getScorings().isEmpty())) {
+            System.out.println("Segment " + segmentIdentifier + " successfully deleted");
+        } else if (validate) {
+            System.out.print("Segment " + segmentIdentifier + " could not be deleted because of the following dependents:");
+            if (!dependantMetadata.getScorings().isEmpty()) {
+                System.out.print(" scoring:" + dependantMetadata.getScorings());
+            }
+            if (!dependantMetadata.getSegments().isEmpty()) {
+                System.out.println(" segments:" + dependantMetadata.getSegments());
+            }
         }
-        String jsonRule = CustomObjectMapper.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(segment);
-        System.out.println(jsonRule);
         return null;
     }
 }
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
index d502148..9016aa9 100644
--- a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/SegmentView.java
@@ -38,7 +38,7 @@ public class SegmentView implements Action {
     public Object execute() throws Exception {
         Segment segment = segmentService.getSegmentDefinition(segmentIdentifier);
         if (segment == null) {
-            System.out.println("Couldn't find an action with id=" + segmentIdentifier);
+            System.out.println("Couldn't find a segment with id=" + segmentIdentifier);
             return null;
         }
         String jsonRule = CustomObjectMapper.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(segment);
diff --git a/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/UndeployDefinition.java b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/UndeployDefinition.java
new file mode 100644
index 0000000..e672bff
--- /dev/null
+++ b/tools/shell-dev-commands/src/main/java/org/apache/unomi/shell/commands/UndeployDefinition.java
@@ -0,0 +1,111 @@
+/*
+ * 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.shell.commands;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.unomi.api.Patch;
+import org.apache.unomi.api.PersonaWithSessions;
+import org.apache.unomi.api.PropertyType;
+import org.apache.unomi.api.actions.ActionType;
+import org.apache.unomi.api.campaigns.Campaign;
+import org.apache.unomi.api.conditions.ConditionType;
+import org.apache.unomi.api.goals.Goal;
+import org.apache.unomi.api.rules.Rule;
+import org.apache.unomi.api.segments.Scoring;
+import org.apache.unomi.api.segments.Segment;
+import org.apache.unomi.persistence.spi.CustomObjectMapper;
+
+import java.io.IOException;
+import java.net.URL;
+
+@Command(scope = "unomi", name = "undeploy-definition", description = "This will undeploy definitions contained in bundles")
+@Service
+public class UndeployDefinition extends DeploymentCommandSupport {
+
+    public void processDefinition(String definitionType, URL definitionURL) {
+        try {
+            if (ALL_OPTION_LABEL.equals(definitionType)) {
+                String definitionURLString = definitionURL.toString();
+                for (String possibleDefinitionType : definitionTypes) {
+                    if (definitionURLString.contains(getDefinitionTypePath(possibleDefinitionType))) {
+                        definitionType = possibleDefinitionType;
+                        break;
+                    }
+                }
+                if (ALL_OPTION_LABEL.equals(definitionType)) {
+                    System.out.println("Couldn't resolve definition type for definition URL " + definitionURL);
+                    return;
+                }
+            }
+            boolean successful = true;
+            switch (definitionType) {
+                case CONDITION_DEFINITION_TYPE:
+                    ConditionType conditionType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, ConditionType.class);
+                    definitionsService.removeActionType(conditionType.getItemId());
+                    break;
+                case ACTION_DEFINITION_TYPE:
+                    ActionType actionType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, ActionType.class);
+                    definitionsService.removeActionType(actionType.getItemId());
+                    break;
+                case GOAL_DEFINITION_TYPE:
+                    Goal goal = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Goal.class);
+                    goalsService.removeGoal(goal.getItemId());
+                    break;
+                case CAMPAIGN_DEFINITION_TYPE:
+                    Campaign campaign = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Campaign.class);
+                    goalsService.removeCampaign(campaign.getItemId());
+                    break;
+                case PERSONA_DEFINITION_TYPE:
+                    PersonaWithSessions persona = CustomObjectMapper.getObjectMapper().readValue(definitionURL, PersonaWithSessions.class);
+                    profileService.delete(persona.getPersona().getItemId(), true);
+                    break;
+                case PROPERTY_DEFINITION_TYPE:
+                    PropertyType propertyType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, PropertyType.class);
+                    profileService.deletePropertyType(propertyType.getItemId());
+                    break;
+                case RULE_DEFINITION_TYPE:
+                    Rule rule = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Rule.class);
+                    rulesService.removeRule(rule.getItemId());
+                    break;
+                case SEGMENT_DEFINITION_TYPE:
+                    Segment segment = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Segment.class);
+                    segmentService.removeSegmentDefinition(segment.getItemId(), false);
+                    break;
+                case SCORING_DEFINITION_TYPE:
+                    Scoring scoring = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Scoring.class);
+                    segmentService.removeScoringDefinition(scoring.getItemId(), false);
+                    break;
+                case PATCH_DEFINITION_TYPE:
+                    Patch patch = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Patch.class);
+                    // patchService.patch(patch);
+                    break;
+                default:
+                    System.out.println("Unrecognized definition type: " + definitionType);
+                    successful = false;
+                    break;
+            }
+            if (successful) {
+                System.out.println("Predefined definition unregistered : " + definitionURL.getFile());
+            }
+        } catch (IOException e) {
+            System.out.println("Error while removing definition " + definitionURL);
+            System.out.println(e.getMessage());
+        }
+    }
+
+}