blob: e89d37d04e62fd366d0491541a560461cc566d9b [file] [log] [blame]
/*
* 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.felix.service.command.CommandSession;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.console.OsgiCommandSupport;
import org.apache.unomi.api.*;
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.api.services.*;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.jline.reader.LineReader;
import org.osgi.framework.Bundle;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Command(scope = "unomi", name = "deploy-definition", description = "This will deploy a specific definition")
public class DeployDefinitionCommand extends OsgiCommandSupport {
private DefinitionsService definitionsService;
private GoalsService goalsService;
private ProfileService profileService;
private RulesService rulesService;
private SegmentService segmentService;
private PatchService patchService;
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;
protected Object doExecute() throws Exception {
Bundle bundleToUpdate;
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);
}
}
String bundleAnswer = askUserWithAuthorizedAnswer(session, "Which bundle ?" + getValuesWithNumber(bundles.stream().map(Bundle::getSymbolicName).collect(Collectors.toList())) + "\n",
IntStream.range(1,bundles.size()+1).mapToObj(Integer::toString).collect(Collectors.toList()));
bundleToUpdate = bundles.get(new Integer(bundleAnswer)-1);
bundleIdentifier = bundleToUpdate.getBundleId();
} else {
bundleToUpdate = bundleContext.getBundle(bundleIdentifier);
}
if (bundleToUpdate == null) {
System.out.println("Couldn't find a bundle with id: " + bundleIdentifier);
return null;
}
if (definitionType == null) {
List<String> values = definitionTypes.stream().filter((t) -> bundleToUpdate.findEntries(getDefinitionTypePath(t), "*.json", true) != null).collect(Collectors.toList());
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);
Enumeration<URL> definitions = bundleToUpdate.findEntries(path, "*.json", true);
if (definitions == null) {
System.out.println("Couldn't find definitions in bundle with id: " + bundleIdentifier + " and definition path: " + path);
return null;
}
List<URL> values = new ArrayList<>();
while (definitions.hasMoreElements()) {
values.add(definitions.nextElement());
}
if (fileName == null) {
List<String> stringList = values.stream().map(u -> StringUtils.substringAfterLast(u.getFile(), "/")).collect(Collectors.toList());
Collections.sort(stringList);
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(CommandSession 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(CommandSession 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 {
switch (definitionType) {
case "condition":
ConditionType conditionType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, ConditionType.class);
definitionsService.setConditionType(conditionType);
break;
case "action":
ActionType actionType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, ActionType.class);
definitionsService.setActionType(actionType);
break;
case "goal":
Goal goal = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Goal.class);
goalsService.setGoal(goal);
break;
case "campaign":
Campaign campaign = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Campaign.class);
goalsService.setCampaign(campaign);
break;
case "persona":
PersonaWithSessions persona = CustomObjectMapper.getObjectMapper().readValue(definitionURL, PersonaWithSessions.class);
profileService.savePersonaWithSessions(persona);
break;
case "property":
PropertyType propertyType = CustomObjectMapper.getObjectMapper().readValue(definitionURL, PropertyType.class);
profileService.setPropertyTypeTarget(definitionURL, propertyType);
profileService.setPropertyType(propertyType);
break;
case "rule":
Rule rule = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Rule.class);
rulesService.setRule(rule);
break;
case "segment":
Segment segment = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Segment.class);
segmentService.setSegmentDefinition(segment);
break;
case "scoring":
Scoring scoring = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Scoring.class);
segmentService.setScoringDefinition(scoring);
break;
case "patch":
Patch patch = CustomObjectMapper.getObjectMapper().readValue(definitionURL, Patch.class);
patchService.patch(patch);
break;
}
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();
}
public void setDefinitionsService(DefinitionsService definitionsService) {
this.definitionsService = definitionsService;
}
public void setGoalsService(GoalsService goalsService) {
this.goalsService = goalsService;
}
public void setProfileService(ProfileService profileService) {
this.profileService = profileService;
}
public void setRulesService(RulesService rulesService) {
this.rulesService = rulesService;
}
public void setSegmentService(SegmentService segmentService) {
this.segmentService = segmentService;
}
public void setPatchService(PatchService patchService) {
this.patchService = patchService;
}
}