doortts doortts 2018-01-29
issue: Issue sharing feature - Build api
@d8143b55c76f63910da0660187cabc946604d50d
app/controllers/api/IssueApi.java
--- app/controllers/api/IssueApi.java
+++ app/controllers/api/IssueApi.java
@@ -385,7 +385,7 @@
         }
     }
 
-    private static void addUserToUsers(User user, List<ObjectNode> users) {
+    static void addUserToUsers(User user, List<ObjectNode> users) {
         ObjectNode userNode = Json.newObject();
         userNode.put("loginId", user.loginId);
         userNode.put("name", user.getDisplayName());
@@ -535,4 +535,128 @@
                     return ok(node);
                 });
     }
+
+    @AnonymousCheck
+    public static Result findSharerByloginIds(String ownerName, String projectName, Long number,
+                                              String commaSeperatedIds) {
+        if (!request().accepts("application/json")) {
+            return status(Http.Status.NOT_ACCEPTABLE);
+        }
+        Project project = Project.findByOwnerAndProjectName(ownerName, projectName);
+        Issue issue = Issue.findByNumber(project, number);
+
+        List<IssueSharer> list = getExpressionListByExtractingLoginIds(issue, commaSeperatedIds).findList();
+        sortListByAddedDate(list);
+
+        List<ObjectNode> users = new ArrayList<>();
+        for (IssueSharer sharer :list) {
+            addUserToUsers(sharer.user, users);
+        }
+        return ok(toJson(users));
+    }
+
+    private static void sortListByAddedDate(List<IssueSharer> list) {
+        list.sort(new Comparator<IssueSharer>() {
+            @Override
+            public int compare(IssueSharer o1, IssueSharer o2) {
+                return o1.created.compareTo(o2.created);
+            }
+        });
+    }
+
+    private static ExpressionList<IssueSharer> getExpressionListByExtractingLoginIds(Issue issue, String query) {
+        String[] queryItems = query.split(",");
+        ExpressionList<IssueSharer> el = IssueSharer.find
+                .where()
+                .in("loginId", Arrays.asList(queryItems))
+                .eq("issue.id", issue.id);
+        return el;
+    }
+
+    @IsAllowed(Operation.READ)
+    public static Result findSharableUsers(String ownerName, String projectName, Long number, String query) {
+        if (!request().accepts("application/json")) {
+            return status(Http.Status.NOT_ACCEPTABLE);
+        }
+
+        List<ObjectNode> users = new ArrayList<>();
+
+        ExpressionList<User> el = getUserExpressionList(query, request().getQueryString("type"));
+
+        int total = el.findRowCount();
+        if (total > MAX_FETCH_USERS) {
+            el.setMaxRows(MAX_FETCH_USERS);
+            response().setHeader("Content-Range", "items " + MAX_FETCH_USERS + "/" + total);
+        }
+
+        for (User user :el.findList()) {
+            addUserToUsers(user, users);
+        }
+
+        return ok(toJson(users));
+    }
+
+    public static Result updateSharer(String owner, String projectName, Long number){
+        JsonNode json = request().body().asJson();
+        if (json == null) {
+            return badRequest(Json.newObject().put("message", "Expecting Json data"));
+        }
+
+        if(noSharer(json.findValue("sharer"))){
+            return badRequest(Json.newObject().put("message", "No sharer"));
+        }
+
+        Project project = Project.findByOwnerAndProjectName(owner, projectName);
+        Issue issue = Issue.findByNumber(project, number);
+        if (!AccessControl.isAllowed(UserApp.currentUser(), issue.asResource(),
+                Operation.UPDATE)) {
+            return forbidden(Json.newObject().put("message", "Permission denied"));
+        }
+
+        ObjectNode result = Json.newObject();
+        String action = json.findValue("action").asText();
+
+        for(JsonNode sharerLoginId: json.findValue("sharer")){
+            switch (action) {
+                case "delete":
+                    removeSharer(issue, sharerLoginId.asText());
+                    result.put("action", "deleted");
+                    break;
+                case "add":
+                    addSharer(issue, sharerLoginId.asText());
+                    result.put("action", "added");
+                    break;
+                default:
+                    result.put("action", "Do nothing");
+            }
+            result.put("sharer", User.findByLoginId(sharerLoginId.asText()).getDisplayName());
+        }
+
+        return ok(result);
+    }
+
+    private static boolean noSharer(JsonNode sharers) {
+        return sharers == null || sharers.size() == 0;
+    }
+
+    private static void addSharer(Issue issue, String loginId) {
+        IssueSharer issueSharer = IssueSharer.find.where()
+                .eq("loginId", loginId)
+                .eq("issue.id", issue.id).findUnique();
+        if(issueSharer == null) {
+            issueSharer = IssueSharer.createSharer(loginId, issue);
+            issueSharer.save();
+        }
+        issue.sharers.add(issueSharer);
+    }
+
+    private static void removeSharer(Issue issue, String loginId) {
+        IssueSharer issueSharer =
+                IssueSharer.find.where()
+                        .eq("loginId", loginId)
+                        .eq("issue.id", issue.id)
+                        .findUnique();
+        issueSharer.delete();
+        issue.sharers.remove(issueSharer);
+    }
 }
app/utils/AccessControl.java
--- app/utils/AccessControl.java
+++ app/utils/AccessControl.java
@@ -27,6 +27,8 @@
 import models.resource.Resource;
 import org.apache.commons.lang.BooleanUtils;
 
+import java.util.Optional;
+
 import static models.OrganizationUser.isAdmin;
 import static models.OrganizationUser.isMember;
 
@@ -114,7 +116,8 @@
             return false;
         }
 
-        if (isAllowedIfAuthor(user, container) || isAllowedIfAssignee(user, container)) {
+        if (isAllowedIfAuthor(user, container) || isAllowedIfAssignee(user, container)
+                || isAllowedIfSharer(user, container)) {
             return true;
         }
 
@@ -285,6 +288,7 @@
         case READ:
             return project.isPublic() && !user.isGuest
                     || user.isMemberOf(project)
+                    || isAllowedIfSharer(user, resource)
                     || isAllowedIfGroupMember(project, user);
         case UPDATE:
             return user.isMemberOf(project)
@@ -374,6 +378,17 @@
         }
     }
 
+    private static boolean isAllowedIfSharer(User user, Resource resource) {
+        switch (resource.getType()) {
+            case ISSUE_POST:
+            case ISSUE_COMMENT:
+                Issue issue = Issue.finder.byId(Long.valueOf(resource.getId()));
+                return issue != null && Optional.ofNullable(issue.findSharerByUserId(user.id)).isPresent();
+            default:
+                return false;
+        }
+    }
+
     /**
      * Checks if an user has a permission to do something to the given
      * resource as an assignee.
conf/routes
--- conf/routes
+++ conf/routes
@@ -56,6 +56,9 @@
 GET            /-_-api/v1/owners/:owner/projects/:projectName/issues/:number/assignableUsers                                                   controllers.api.IssueApi.findAssignableUsers(owner:String, projectName:String, number:Long, query: String ?= "")
 GET            /-_-api/v1/owners/:owner/projects/:projectName/assignableUsers                                                   controllers.api.IssueApi.findAssignableUsersOfProject(owner:String, projectName:String, query: String ?= "")
 POST           /-_-api/v1/owners/:owner/projects/:projectName/issues/:number/assignees                                                   controllers.api.IssueApi.updateAssginees(owner:String, projectName:String, number:Long)
+GET            /-_-api/v1/owners/:owner/projects/:projectName/issues/:number/findSharer                                              controllers.api.IssueApi.findSharerByloginIds(owner:String, projectName:String, number:Long, query: String ?= "")
+GET            /-_-api/v1/owners/:owner/projects/:projectName/issues/:number/sharableUsers                                                   controllers.api.IssueApi.findSharableUsers(owner:String, projectName:String, number:Long, query: String ?= "")
+POST           /-_-api/v1/owners/:owner/projects/:projectName/issues/:number/share                                                   controllers.api.IssueApi.updateSharer(owner:String, projectName:String, number:Long)
 GET            /-_-api/v1/users                                                        controllers.UserApp.users(query: String ?= "")
 POST           /-_-api/v1/users                                                        controllers.api.UserApi.newUser()
 POST           /-_-api/v1/owners/:owner/projects                                       controllers.api.ProjectApi.newProject(owner:String)
Add a comment
List