mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-12 05:35:31 +00:00
Bug 4104: Improved REST API (all CRUD operations are now available)
This commit is contained in:
1 parent
413bf691d7
commit
df59662141
4 files changed
+193
-102
No files matched your search
-10
@@ -96,14 +96,4 @@ public abstract class AbstractSailingServerResource {
|
||||
bigDecimal = bigDecimal.setScale(places, RoundingMode.HALF_UP);
|
||||
return bigDecimal.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores original URL parameter which uses the escape "__" for "/"
|
||||
*
|
||||
* @param toRestore url parameter as string containing "__" instead of "/"
|
||||
* @return restored url parameter containing "/" instead of "__"
|
||||
*/
|
||||
protected String restoreEscapedParameter(String toRestore) {
|
||||
return toRestore != null && toRestore.contains(SLASH_ENCODING) ? toRestore.replace(SLASH_ENCODING, "/") : toRestore;
|
||||
}
|
||||
}
|
||||
+140
-69
@@ -4,106 +4,177 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.FormParam;
|
||||
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.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogTagEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.TagFinder;
|
||||
import com.sap.sailing.domain.common.dto.TagDTO;
|
||||
import com.sap.sailing.domain.common.racelog.RaceLogServletConstants;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sailing.server.tagging.TagDTODeSerializer;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.UserStore;
|
||||
import com.sap.sailing.server.tagging.TaggingService;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
@Path("/v1/tags")
|
||||
public class TagsResource extends AbstractSailingServerResource {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TagsResource.class.getName());
|
||||
private static final String APPLICATION_JSON_UTF8 = MediaType.APPLICATION_JSON + ";charset=UTF-8";
|
||||
private static final String TEXT_PLAIN_UTF8 = MediaType.TEXT_PLAIN + ";charset=UTF-8";
|
||||
|
||||
private final TagDTODeSerializer serializer;
|
||||
// private final boolean enforceSecurityChecks;
|
||||
|
||||
public TagsResource() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public TagsResource(boolean enforceSecurityChecks) {
|
||||
// this.enforceSecurityChecks = enforceSecurityChecks;
|
||||
serializer = new TagDTODeSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads public tags from {@link RaceLog} and private tags from {@link UserStore} in case current user is logged in.
|
||||
* Only loads private tags when parameters <code>leaderboardName</code>, <code>raceColumnName</code> and
|
||||
* <code>fleetName</code> can identify {@link RaceLog}.
|
||||
* Loads public tags from {@link com.sap.sailing.domain.abstractlog.race.RaceLog RaceLog} and private tags from
|
||||
* {@link com.sap.sse.security.UserStore UserStore} in case current user is logged in. Only loads tags when
|
||||
* parameters <code>leaderboardName</code>, <code>raceColumnName</code> and <code>fleetName</code> can identify
|
||||
* {@link com.sap.sailing.domain.abstractlog.race.RaceLog RaceLog}.
|
||||
*
|
||||
* @return status 200 (including tags) if request was successful, otherwise 400 (bad request) or 500 (internal
|
||||
* server error)
|
||||
*/
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("/{leaderboardName}/{raceColumnName}/{fleetName}")
|
||||
public Response getTags(@PathParam("leaderboardName") String leaderboardNameParameter,
|
||||
@PathParam("raceColumnName") String raceColumnNameParameter,
|
||||
@PathParam("fleetName") String fleetNameParameter) {
|
||||
// restore escaped parameters
|
||||
String leaderboardName = restoreEscapedParameter(leaderboardNameParameter);
|
||||
String raceColumnName = restoreEscapedParameter(raceColumnNameParameter);
|
||||
String fleetName = restoreEscapedParameter(fleetNameParameter);
|
||||
@Produces({ APPLICATION_JSON_UTF8, TEXT_PLAIN_UTF8 })
|
||||
public Response getTags(@QueryParam(RaceLogServletConstants.PARAMS_LEADERBOARD_NAME) String leaderboardName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME) String raceColumnName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME) String fleetName) {
|
||||
// TODO: Allow to get only public, only private tags or both of them
|
||||
// TODO: Allow to get only unrevoked tags
|
||||
Response response;
|
||||
TaggingService taggingService = getService().getTaggingService();
|
||||
if (taggingService != null) {
|
||||
List<TagDTO> tags = new ArrayList<TagDTO>();
|
||||
|
||||
// default response
|
||||
Response response = Response.noContent().build();
|
||||
List<TagDTO> tags = new ArrayList<TagDTO>();
|
||||
List<TagDTO> publicTags = taggingService.getPublicTags(leaderboardName, raceColumnName, fleetName);
|
||||
Util.addAll(publicTags, tags);
|
||||
|
||||
// check if RaceLog is available and all parameters are set
|
||||
RaceLog raceLog = getService().getRaceLog(leaderboardName, raceColumnName, fleetName);
|
||||
if (raceLog == null) {
|
||||
response = Response.status(Status.PRECONDITION_FAILED)
|
||||
.entity("RaceLog not found! Check parameters leaderboardName, raceColumnName and fleetName")
|
||||
.build();
|
||||
} else {
|
||||
// load public tags
|
||||
TagFinder finder = new TagFinder(raceLog, true);
|
||||
List<RaceLogTagEvent> publicTags = finder.analyze();
|
||||
for (RaceLogTagEvent tagEvent : publicTags) {
|
||||
tags.add(new TagDTO(tagEvent.getTag(), tagEvent.getComment(), tagEvent.getImageURL(),
|
||||
tagEvent.getUsername(), true, tagEvent.getLogicalTimePoint(), tagEvent.getCreatedAt()));
|
||||
}
|
||||
List<TagDTO> privateTags = taggingService.getPrivateTags(leaderboardName, raceColumnName, fleetName);
|
||||
Util.addAll(privateTags, tags);
|
||||
|
||||
// load private tags
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
if (subject.getPrincipal() != null) {
|
||||
String username = subject.getPrincipal().toString();
|
||||
SecurityService securityService = getService(SecurityService.class);
|
||||
|
||||
// TODO: check preferences key in home and set private tag
|
||||
String preference = securityService.getPreference(username,
|
||||
serializer.generateUniqueKey(leaderboardName, raceColumnName, fleetName));
|
||||
if (preference != null) {
|
||||
try {
|
||||
JSONParser jsonParser = new JSONParser();
|
||||
JSONArray jsonArray = (JSONArray) jsonParser.parse(preference);
|
||||
List<TagDTO> privateTags = serializer.deserialize(jsonArray);
|
||||
tags.addAll(privateTags);
|
||||
} catch (ParseException e) {
|
||||
logger.warning("Could not parse private tags received from JSON in UserStore!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert tags to JSON
|
||||
JSONArray jsonTags = serializer.serialize(tags);
|
||||
response = Response.ok(jsonTags.toJSONString()).build();
|
||||
response = Response.ok(jsonTags.toJSONString()).type(APPLICATION_JSON_UTF8).build();
|
||||
} else {
|
||||
response = Response.status(Status.INTERNAL_SERVER_ERROR).type(TEXT_PLAIN_UTF8)
|
||||
.entity("Tagging Service not found!").build();
|
||||
logger.warning("Tagging Service not found!");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves new tag.
|
||||
*
|
||||
* @param tag
|
||||
* may not be empty
|
||||
* @param visible
|
||||
* default is <code>false</code>
|
||||
*
|
||||
* @return status 201 (created) if creation was successful, otherwise 400 (bad request)
|
||||
* @see TaggingService#addTag(String, String, String, String, String, String, boolean, TimePoint)
|
||||
*/
|
||||
@POST
|
||||
@Produces({ APPLICATION_JSON_UTF8, TEXT_PLAIN_UTF8 })
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public Response createTag(@Context UriInfo uriInfo,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_LEADERBOARD_NAME) String leaderboardName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME) String raceColumnName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME) String fleetName, @FormParam("tag") String tag,
|
||||
@FormParam("comment") String comment, @FormParam("image") String imageURL,
|
||||
@FormParam("public") boolean visibleForPublic, @FormParam("raceTimepoint") long raceTimepoint) {
|
||||
Response response;
|
||||
TaggingService taggingService = getService().getTaggingService();
|
||||
boolean successful = taggingService.addTag(leaderboardName, raceColumnName, fleetName, tag, comment, imageURL,
|
||||
visibleForPublic, new MillisecondsTimePoint(raceTimepoint));
|
||||
if (successful) {
|
||||
response = Response.created(uriInfo.getRequestUri()).build();
|
||||
} else {
|
||||
String errorMessage = taggingService.getLastErrorCode().getMessage();
|
||||
response = Response.status(Status.BAD_REQUEST).type(TEXT_PLAIN_UTF8).entity(errorMessage).build();
|
||||
logger.warning("Could not save tag! " + errorMessage);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes tag given as json string.
|
||||
*
|
||||
* @return status 204 (no content) if deletion was successful, otherwise 400 (bad request)
|
||||
* @see TagDTO
|
||||
* @see TaggingService#removeTag(String, String, String, TagDTO)
|
||||
*/
|
||||
@DELETE
|
||||
@Produces({ APPLICATION_JSON_UTF8, TEXT_PLAIN_UTF8 })
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public Response deleteTag(@QueryParam(RaceLogServletConstants.PARAMS_LEADERBOARD_NAME) String leaderboardName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME) String raceColumnName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME) String fleetName,
|
||||
@FormParam("tag_json") String tagJson) {
|
||||
Response response;
|
||||
TagDTO tagToRemove = serializer.deserializeTag(tagJson);
|
||||
TaggingService taggingService = getService().getTaggingService();
|
||||
boolean successful = taggingService.removeTag(leaderboardName, raceColumnName, fleetName, tagToRemove);
|
||||
if (successful) {
|
||||
response = Response.noContent().build();
|
||||
} else {
|
||||
String errorMessage = taggingService.getLastErrorCode().getMessage();
|
||||
response = Response.status(Status.BAD_REQUEST).type(TEXT_PLAIN_UTF8).entity(errorMessage).build();
|
||||
logger.warning("Could not remove tag! " + errorMessage);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates tag.
|
||||
*
|
||||
* @param tag
|
||||
* may not be empty
|
||||
* @param visible
|
||||
* default is <code>false</code>
|
||||
*
|
||||
* @return status 204 (no content) if update was successful, otherwise 400 (bad request)
|
||||
* @see TagDTO
|
||||
* @see TaggingService#updateTag(String, String, String, TagDTO, String, String, String, boolean)
|
||||
*/
|
||||
@PUT
|
||||
@Produces({ APPLICATION_JSON_UTF8, TEXT_PLAIN_UTF8 })
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public Response updateTag(@QueryParam(RaceLogServletConstants.PARAMS_LEADERBOARD_NAME) String leaderboardName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME) String raceColumnName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME) String fleetName,
|
||||
@FormParam("tag_json") String tagJson, @FormParam("tag") String tag, @FormParam("comment") String comment,
|
||||
@FormParam("image") String imageURL, @FormParam("public") boolean visibleForPublic) {
|
||||
Response response;
|
||||
TagDTO tagToUpdate = serializer.deserializeTag(tagJson);
|
||||
TaggingService taggingService = getService().getTaggingService();
|
||||
boolean successful = taggingService.updateTag(leaderboardName, raceColumnName, fleetName, tagToUpdate, tag,
|
||||
comment, imageURL, visibleForPublic);
|
||||
if (successful) {
|
||||
response = Response.noContent().build();
|
||||
} else {
|
||||
String errorMessage = taggingService.getLastErrorCode().getMessage();
|
||||
response = Response.status(Status.BAD_REQUEST).type(TEXT_PLAIN_UTF8).entity(errorMessage).build();
|
||||
logger.warning("Could not update tag! " + errorMessage);
|
||||
}
|
||||
return response;
|
||||
|
||||
}
|
||||
}
|
||||
+18
-11
@@ -13,23 +13,30 @@ public interface TaggingService {
|
||||
* Enum used to identify issues.
|
||||
*/
|
||||
public enum ErrorCode {
|
||||
UNKNOWN_ERROR("Unknown error"),
|
||||
USER_NOT_FOUND("User not found"),
|
||||
MISSING_PERMISSIONS("Missing permissions"),
|
||||
SECURITY_SERIVCE_NOT_FOUND("Security service not found"),
|
||||
RACELOG_NOT_FOUND("Racelog not found"),
|
||||
TAG_NOT_REVOKABLE("This tag cannot be revoked"),
|
||||
TAG_NOT_EMPTY("Tag may not be empty"),
|
||||
TIMEPOINT_NOT_EMPTY("Timepoint may not be empty"),
|
||||
TAG_TOO_LONG("Tag is too long"),
|
||||
COMMENT_TOO_LONG("Comment is too long");
|
||||
UNKNOWN_ERROR("unknownError", "Unknown error"),
|
||||
NOT_LOGGED_IN("notLoggedin", "You are not logged in"),
|
||||
MISSING_PERMISSIONS("missingPermissions", "Missing permissions"),
|
||||
SECURITY_SERIVCE_NOT_FOUND("securityServiceNotFound", "Security service not found"),
|
||||
RACELOG_NOT_FOUND("racelogNotFound", "Racelog not found"),
|
||||
TAG_NOT_REVOKABLE("tagNotRevokable", "This tag cannot be revoked"),
|
||||
TAG_ALREADY_EXISTS("tagAlreadyExists", "Tag does already exist, duplicated tags are not allowed"),
|
||||
TAG_NOT_EMPTY("tagNotEmpty", "Tag may not be empty"),
|
||||
TIMEPOINT_NOT_EMPTY("timepointNotEmpty", "Timepoint may not be empty"),
|
||||
TAG_TOO_LONG("tagTooLong", "Tag is too long"),
|
||||
COMMENT_TOO_LONG("commentToLong", "Comment is too long");
|
||||
|
||||
private final String code;
|
||||
private final String message;
|
||||
|
||||
ErrorCode(String message) {
|
||||
ErrorCode(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
+35
-12
@@ -48,7 +48,7 @@ public class TaggingServiceImpl implements TaggingService {
|
||||
if (principal != null) {
|
||||
result = principal.toString();
|
||||
} else {
|
||||
setLastErrorCode(ErrorCode.USER_NOT_FOUND);
|
||||
setLastErrorCode(ErrorCode.NOT_LOGGED_IN);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -75,8 +75,23 @@ public class TaggingServiceImpl implements TaggingService {
|
||||
setLastErrorCode(ErrorCode.RACELOG_NOT_FOUND);
|
||||
successful = false;
|
||||
} else {
|
||||
raceLog.add(new RaceLogTagEventImpl(tag, comment, imageURL, raceTimepoint,
|
||||
racingService.getServerAuthor(), raceLog.getCurrentPassId()));
|
||||
// check if tag already exists
|
||||
boolean alreadyExists = false;
|
||||
List<TagDTO> publicTags = getPublicTags(leaderboardName, raceColumnName, fleetName);
|
||||
for (TagDTO publicTag : publicTags) {
|
||||
if (publicTag.equals(tag, comment, imageURL, racingService.getServerAuthor().getName(), true,
|
||||
raceTimepoint)) {
|
||||
alreadyExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (alreadyExists) {
|
||||
setLastErrorCode(ErrorCode.TAG_ALREADY_EXISTS);
|
||||
successful = false;
|
||||
} else {
|
||||
raceLog.add(new RaceLogTagEventImpl(tag, comment, imageURL, raceTimepoint,
|
||||
racingService.getServerAuthor(), raceLog.getCurrentPassId()));
|
||||
}
|
||||
}
|
||||
} catch (AuthorizationException e) {
|
||||
setLastErrorCode(ErrorCode.MISSING_PERMISSIONS);
|
||||
@@ -101,11 +116,17 @@ public class TaggingServiceImpl implements TaggingService {
|
||||
String key = serializer.generateUniqueKey(leaderboardName, raceColumnName, fleetName);
|
||||
String privateTagsJson = securityService.getPreference(username, key);
|
||||
List<TagDTO> privateTags = serializer.deserializeTags(privateTagsJson);
|
||||
privateTags.add(new TagDTO(tag, comment, imageURL, username, false, raceTimepoint,
|
||||
MillisecondsTimePoint.now()));
|
||||
securityService.setPreference(username, key, serializer.serializeTags(privateTags));
|
||||
TagDTO tagToAdd = new TagDTO(tag, comment, imageURL, username, false, raceTimepoint,
|
||||
MillisecondsTimePoint.now());
|
||||
if (privateTags.contains(tagToAdd)) {
|
||||
setLastErrorCode(ErrorCode.TAG_ALREADY_EXISTS);
|
||||
successful = false;
|
||||
} else {
|
||||
privateTags.add(tagToAdd);
|
||||
securityService.setPreference(username, key, serializer.serializeTags(privateTags));
|
||||
}
|
||||
} else {
|
||||
setLastErrorCode(ErrorCode.USER_NOT_FOUND);
|
||||
setLastErrorCode(ErrorCode.NOT_LOGGED_IN);
|
||||
successful = false;
|
||||
}
|
||||
}
|
||||
@@ -159,19 +180,21 @@ public class TaggingServiceImpl implements TaggingService {
|
||||
private boolean removePrivateTag(String leaderboardName, String raceColumnName, String fleetName, TagDTO tag) {
|
||||
boolean successful = true;
|
||||
|
||||
List<TagDTO> privateTags = getPrivateTags(leaderboardName, raceColumnName, fleetName);
|
||||
if (privateTags != null) {
|
||||
String username = getCurrentUsername();
|
||||
if (username == null) {
|
||||
setLastErrorCode(ErrorCode.NOT_LOGGED_IN);
|
||||
successful = false;
|
||||
} else {
|
||||
List<TagDTO> privateTags = getPrivateTags(leaderboardName, raceColumnName, fleetName);
|
||||
privateTags.remove(tag);
|
||||
String username = getCurrentUsername();
|
||||
SecurityService securityService = getSecurityService();
|
||||
String key = serializer.generateUniqueKey(leaderboardName, raceColumnName, fleetName);
|
||||
// error code will be set during collection of required data
|
||||
if (username != null && securityService != null && key != null) {
|
||||
securityService.setPreference(username, key, serializer.serializeTags(privateTags));
|
||||
}
|
||||
} else {
|
||||
successful = false;
|
||||
}
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user