package info.interactivesystems.gamificationengine.api;
import info.interactivesystems.gamificationengine.api.validation.ValidApiKey;
import info.interactivesystems.gamificationengine.api.validation.ValidListOfDigits;
import info.interactivesystems.gamificationengine.api.validation.ValidListOfDigitsOrNull;
import info.interactivesystems.gamificationengine.api.validation.ValidPositiveDigit;
import info.interactivesystems.gamificationengine.dao.GoalDAO;
import info.interactivesystems.gamificationengine.dao.OrganisationDAO;
import info.interactivesystems.gamificationengine.dao.RewardDAO;
import info.interactivesystems.gamificationengine.dao.RoleDAO;
import info.interactivesystems.gamificationengine.dao.RuleDAO;
import info.interactivesystems.gamificationengine.entities.Organisation;
import info.interactivesystems.gamificationengine.entities.Role;
import info.interactivesystems.gamificationengine.entities.goal.Goal;
import info.interactivesystems.gamificationengine.entities.goal.GoalRule;
import info.interactivesystems.gamificationengine.entities.rewards.Reward;
import info.interactivesystems.gamificationengine.utils.StringUtils;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
/**
* A Goal comprises one or more tasks and has to be completed if the player wants to earn the connected awards.
* To create a goal some already created components are needed. So the condition when a goal is completed is
* defined in the goal rule and the connected tasks. Who can complete a goal is defined by the role of a player
* and whether it can be done by a group. It is also possible to define whether a goal is repeatable so that the
* player can complete the tasks and obtains its coins and points as rewards again. All goals that are
* associated with the organisation can be requested or like the elements before only one specific goal, if the
* correspondent id is used. The name, the associated rewards and also the rule for completion can be changed
* as well as the indication if the goal is repeatable or a goal that can be reached by a group. It is also
* possible to change the roles so different people can complete the goal.
*/
@Path("/goal")
@Stateless
@Produces(MediaType.APPLICATION_JSON)
public class GoalApi {
private static final Logger LOGGER = LoggerFactory.getLogger(GoalApi.class);
@Inject
OrganisationDAO organisationDao;
@Inject
GoalDAO goalDao;
@Inject
RuleDAO ruleDao;
@Inject
RewardDAO rewardDao;
@Inject
RoleDAO roleDao;
/**
* Creates a new goal and so the method generates the goal-id.
* The organisation's API key is mandatory otherwise a warning with the hint for a non valid API key is
* returned.
* By the creation the name and the id of the associated rule are needed. It can also be defined if
* the goal is repeatable or if it can also be completed by a group.
* Optionally the goal can be passed the ids of roles which are allowed to complete the goal. So if a player has at
* least one of these roles she/he can complete the goal and earn its rewards. It is checked, if the ids of the
* players are positive numbers otherwise a message for the invalid number is returned.
* Optionally the goal can be passed the id of rewards which can be earned. These ids are also checked if
* they are positive numbers.
* If the API key is not valid an analogous message is returned.
*
* Note: If a goal is associated with a points rule and is also repeatable the goal will be added once only to the
* player's or respectively group's list of already finished goals. The rewards of such a goal are also awarded
* only once. So a points rule can be fulfilled once only although the associated goal is repeatable.
*
* @param name
* The name of the goal. This parameter is required.
* @param repeatable
* Optionally a goal can be set as repeatable by "1" or "0", "true" or
* "false". The default value is "true".
* @param ruleId
* The rule which define when a goal is completed. This parameter is required.
* @param rewardIds
* All rewards that are awarded to the player who completes the goal. These ids are
* separated by commas.
* @param roleIds
* Optionally a list of role-ids can be passed which are separated by commas. These ids indicate
* who is allowed to fulfil the goal. This parameter is required.
* @param isGroupGoal
* Optionally a goal can also be done by a group. Possible values are "1" or "0", "true" or
* "false". The default value is "false".
* @param apiKey
* The valid query parameter API key affiliated to one specific organisation,
* to which this goal belongs to.
* @return A Response of Goal in JSON.
*/
@POST
@Path("/")
@TypeHint(Goal.class)
public Response createNewGoal(@QueryParam("name") @NotNull String name, @QueryParam("repeatable") @DefaultValue("true") String repeatable,
@QueryParam("ruleId") @NotNull @ValidPositiveDigit String ruleId,
@QueryParam("rewardIds") @NotNull @ValidListOfDigits String rewardIds,
@QueryParam("roleIds") @DefaultValue("null") @ValidListOfDigitsOrNull String roleIds,
@QueryParam("groupGoal") @DefaultValue("false") String isGroupGoal, @QueryParam("apiKey") @ValidApiKey String apiKey) {
Goal.logGoalDetails(name, repeatable, ruleId, rewardIds, roleIds, isGroupGoal, apiKey);
Organisation organisation = organisationDao.getOrganisationByApiKey(apiKey);
Goal goal = new Goal();
goal.setName(name);
goal.setBelongsTo(organisation);
// Convert String to boolean
boolean isRepeatable = StringUtils.checkBoolean(repeatable);
goal.setRepeatable(isRepeatable);
boolean isPlayerGroupGoal = StringUtils.checkBoolean(isGroupGoal);
goal.setPlayerGroupGoal(isPlayerGroupGoal);
// Get rule object
int rId = ValidateUtils.requireGreaterThanZero(ruleId);
GoalRule rule = ruleDao.getRule(rId, apiKey);
ValidateUtils.requireNotNull(rId, rule);
goal.setRule(rule);
// Find all rewards by Id
String[] rewardIdList = rewardIds.split(",");
for (String rewardIdString : rewardIdList) {
LOGGER.debug("RewardToAdd: " + rewardIdString);
Reward reward = rewardDao.getReward(ValidateUtils.requireGreaterThanZero(rewardIdString), apiKey);
if (reward != null) {
LOGGER.debug("RewardAdded: " + reward.getId());
goal.addReward(reward);
}
}
// Find all roles by Id and Organisation
List<Role> roles = new ArrayList<>();
if(!"null".equals(roleIds)){
String[] rolesList = roleIds.split(",");
for (String roleIdString : rolesList) {
Role role = roleDao.getRole(ValidateUtils.requireGreaterThanZero(roleIdString), apiKey);
if (role != null) {
roles.add(role);
}
}
}
goal.setCanCompletedBy(roles);
goalDao.insertGoal(goal);
return ResponseSurrogate.created(goal);
}
/**
* Returns all goals which are associated with the given API key and so are belonging to the organisation.
* The players of one organisaiton can try to complete one these goals.
* If the API key is not valid an analogous message is returned.
*
* @param apiKey
* The valid query parameter API key affiliated to one specific organisation,
* to which this goal belongs to.
* @return A Response as List of Goals in JSON.
*/
@GET
@Path("/*")
@TypeHint(Goal[].class)
public Response getGoals(@QueryParam("apiKey") @ValidApiKey String apiKey) {
List<Goal> goals = goalDao.getGoals(apiKey);
return ResponseSurrogate.of(goals);
}
/**
* Gets the {@link GoalRule} object which is associated with the goal. It is identified by the passed id and
* the API key. If the API key is not valid an analogous message is returned. It is also checked, if the
* id is a positive number otherwise a message for an invalid number is returned..
*
* @param id
* Required integer which uniquely identify the {@link Goal}.
* @param apiKey
* The valid query parameter API key affiliated to one specific organisation,
* to which this goal belongs to.
* @return Response of Goal in JSON.
*/
@GET
@Path("/{id}")
@TypeHint(Goal.class)
public Response getGoal(@PathParam("id") @NotNull @ValidPositiveDigit String id, @QueryParam("apiKey") @ValidApiKey String apiKey) {
int goalId = ValidateUtils.requireGreaterThanZero(id);
Goal goal = goalDao.getGoal(goalId, apiKey);
ValidateUtils.requireNotNull(goalId, goal);
return ResponseSurrogate.of(goal);
}
/**
* With this method the fields of one specific goal can be changed. For this the goal id, the API key of
* the specific organisation, the name of the field and the new field's value are needed.
* To modify the name of the goal the new string has to be transfered with the attribute field.
* A list with role-ids separated by commas can be passed to define new roles which a player has to be allowed
* to complete the goal. By passing an id of another rule a new goal rule is associated with the goal.
* To modify if a goal is repeatable or can be completed as a group the values "1" or "0" or alternatively
* "true" and "false" can be passed.
* It is also checked, if all ids are a positive number otherwise a message for an invalid number is returned.
* If the API key is not valid an analogous message is returned.
*
* @param goalId
* Required id of the goal which should be modified.
* @param attribute
* The attribute which should be modified. This parameter is required.
* The following names of attributes can be used to change the associated field:
* "goalName", "isRepeateable", "isGroupGoal", "rewardId" and "roles".
* @param value
* The new value of the attribute.
* @param apiKey
* The valid query parameter API key affiliated to one specific organisation,
* to which this role belongs to.
* @return Response of Goal in JSON.
*/
@PUT
@Path("/{id}/attributes")
@TypeHint(Goal.class)
public Response changeGoalAttributes(@PathParam("id") @NotNull @ValidPositiveDigit String goalId,
@QueryParam("attribute") String attribute,
@QueryParam("value") String value, @QueryParam("apiKey") @ValidApiKey String apiKey) {
LOGGER.debug("change Attribute of Goal");
Goal goal = goalDao.getGoal(ValidateUtils.requireGreaterThanZero(goalId), apiKey);
ValidateUtils.requireNotNull(Integer.valueOf(goalId),goal);
if ("null".equals(value) || value != null && value.isEmpty()) {
value = null;
}
switch (attribute) {
case "goalName":
goal.setName(value);
break;
case "isRepeateable":
goal.setRepeatable(Boolean.parseBoolean(value));
break;
case "isGroupGoal":
goal.setPlayerGroupGoal(Boolean.parseBoolean(value));
break;
case "rewardId":
changeRewardIds(value, goal, apiKey);
break;
case "roles":
changeRoles(value, goal, apiKey);
break;
default:
break;
}
goalDao.insertGoal(goal);
return ResponseSurrogate.created(goal);
}
/**
* This method converts the string of reward ids which are transfered to a list of rewards.
* These rewards are then set as the new list of rewards a player can earn by completing the goal.
*
* @param value
* The new values of rewards as string separated by commas. This parameter is required.
* @param organisation
* The organisation the goal belongs to and which is represented by the API key..
* @param goal
* The goal whose field of rewards will be modified. This parameter should be not
* null.
* @param apiKey
* The valid query parameter API key affiliated to one specific organisation,
* to which this goal belongs to.
*/
private void changeRewardIds(@NotNull String value, Goal goal, String apiKey) {
String commaSeparatedList = StringUtils.validateAsListOfDigits(value);
List<Integer> ids = StringUtils.stringArrayToIntegerList(commaSeparatedList);
List<Reward> rewards = rewardDao.getRewards(ids, apiKey);
goal.setRewards(rewards);
}
/**
* This method converts the string of role-ids which are transfered to a list of roles.
* These roles are then set as the new list of roles a player can have to be allowed to complete a goal.
*
* @param value
* The new values of roles as string separated by commas. This parameter is required.
* @param goal
* The goal whose field of roles will be modified. This parameter should be not
* null.
* @param apiKey
* The valid query parameter API key affiliated to one specific organisation,
* to which this goal belongs to.
*/
private void changeRoles(@NotNull String value, Goal goal, @NotNull String apiKey) {
String commaSeparatedList = StringUtils.validateAsListOfDigits(value);
List<Integer> ids = StringUtils.stringArrayToIntegerList(commaSeparatedList);
List<Role> roles = roleDao.getRoles(ids, apiKey);
goal.setCanCompletedBy(roles);
}
/**
* Removes a specific goal from the data base which is identified by the given id and the
* API key. If the API key is not valid an analogous message is returned. It is also checked,
* if the id is a positive number otherwise a message for an invalid number is returned.
*
* @param id
* Required integer which uniquely identify the {@link Goal}.
* @param apiKey
* The valid query parameter API key affiliated to one specific organisation,
* to which this goal belongs to.
* @return Response of Goal in JSON.
*/
@DELETE
@Path("/{id}")
@TypeHint(Goal.class)
public Response deleteGoal(@PathParam("id") @NotNull @ValidPositiveDigit String id,
@QueryParam("apiKey") @ValidApiKey String apiKey) {
int goalId = ValidateUtils.requireGreaterThanZero(id);
Goal goal = goalDao.getGoal(goalId, apiKey);
ValidateUtils.requireNotNull(goalId, goal);
goal = goalDao.deleteGoal(goal, apiKey);
return ResponseSurrogate.deleted(goal);
}
}