Add BoardService implementation. #3

Merged
UmbertoF merged 10 commits from feature_board into develop 2019-11-10 22:44:41 +00:00
16 changed files with 861 additions and 22 deletions

View File

@ -9,6 +9,8 @@ dependencies {
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.retrofit2:converter-gson:$retrofitVersion")
// mock server response
testImplementation("com.squareup.okhttp3:mockwebserver:4.2.1")
// use JUnit test framework
testImplementation("junit:junit:4.12")
}

View File

@ -0,0 +1,86 @@
package wekan.wrapper.api;
import retrofit2.Call;
import retrofit2.http.*;
import wekan.wrapper.entity.Board;
import wekan.wrapper.entity.LabelPrototype;
import wekan.wrapper.entity.MemberPermission;
import wekan.wrapper.entity.BoardPrototype;
import java.util.List;
public interface BoardService {
/**
* Get all public boards.
*
* @return the list with all public boards
*/
@GET("api/boards")
Call<List<Board>> getPublicBoards();
/**
* Create a board.
*
* @param boardPrototype the prototype of the new board
* @return the new board
* @see BoardPrototype
*/
@POST("api/boards")
Call<Board> newBoard(@Body BoardPrototype boardPrototype);
/**
* Get the board with that particular ID.
*
* @param boardId The ID of the board
* @return The board with the matching ID
*/
@GET("api/boards/{boardId}")
Call<Board> getBoard(@Path("boardId") String boardId);
/**
* Delete the board with that particular ID
*
* @param boardId The ID of the board
*/
@DELETE("api/boards/{boardId}")
Call<Void> deleteBoard(@Path("boardId") String boardId);
/**
* Add a label to a board.
*
* @param boardId The ID of the board
* @param labelPrototype The prototype of new label
* @return The ID of new label
* @see LabelPrototype
*/
@PUT("/api/boards/{boardId}/labels")
Call<String> addLabel(
@Path("boardId") String boardId,
@Body LabelPrototype labelPrototype
);
/**
* Change the permission of a member of a board.
*
* @param boardId The ID of the board
* @param memberId The ID of the member
* @param memberPermission The new role of the member
* @see MemberPermission
*/
@POST("/api/boards/{boardId}/members/{memberId}")
Call<Void> setMemberPermission(
@Path("boardId") String boardId,
@Path("memberId") String memberId,
@Body MemberPermission memberPermission
);
/**
* Get all boards attached to a user.
*
* @param userId The ID of the user
* @return The list with all user's boards
*/
@GET("api/users/{userId}/boards")
Call<List<Board>> getBoardsFromUser(@Path("userId") String userId);
}

View File

@ -1,4 +0,0 @@
package wekan.wrapper.api;
public interface ExampleApi {
}

View File

@ -0,0 +1,155 @@
package wekan.wrapper.entity;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
public class Board {
@SerializedName("_id")
private String id;
private String title;
private String slug;
private boolean archived;
private Date createdAt;
private Date modifiedAt;
private int starts;
private List<Label> labels;
private List<Member> members;
private BoardPermission permission;
@SerializedName("color")
private BoardBackgroundColor backgroundColor;
private String description;
private String subtasksDefaultBoardId;
private String subtasksDefaultListId;
private boolean allowsSubtasks;
private PresentParentTask presentParentTask;
private Date startAt;
private Date dueAt;
private Date endAt;
private int spentTime;
private boolean isOvertime;
private String type;
private String defaultSwimlaneId;
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public String getSlug() {
return slug;
}
public boolean isArchived() {
return archived;
}
public Date getCreatedAt() {
return createdAt;
}
public Date getModifiedAt() {
return modifiedAt;
}
public int getStarts() {
return starts;
}
public List<Label> getLabels() {
return labels;
}
public List<Member> getMembers() {
return members;
}
public BoardPermission getPermission() {
return permission;
}
public BoardBackgroundColor getBackgroundColor() {
return backgroundColor;
}
public String getDescription() {
return description;
}
public String getSubtasksDefaultBoardId() {
return subtasksDefaultBoardId;
}
public String getSubtasksDefaultListId() {
return subtasksDefaultListId;
}
public boolean isAllowsSubtasks() {
return allowsSubtasks;
}
public PresentParentTask getPresentParentTask() {
return presentParentTask;
}
public Date getStartAt() {
return startAt;
}
public Date getDueAt() {
return dueAt;
}
public Date getEndAt() {
return endAt;
}
public int getSpentTime() {
return spentTime;
}
public boolean isOvertime() {
return isOvertime;
}
public String getType() {
return type;
}
public String getDefaultSwimlaneId() {
return defaultSwimlaneId;
}
@Override
public String toString() {
return "Board{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
", slug='" + slug + '\'' +
", archived=" + archived +
", createdAt=" + createdAt +
", modifiedAt=" + modifiedAt +
", starts=" + starts +
", labels=" + labels +
", members=" + members +
", permission=" + permission +
", backgroundColor=" + backgroundColor +
", description='" + description + '\'' +
", subtasksDefaultBoardId='" + subtasksDefaultBoardId + '\'' +
", subtasksDefaultListId='" + subtasksDefaultListId + '\'' +
", allowsSubtasks=" + allowsSubtasks +
", presentParentTask=" + presentParentTask +
", startAt=" + startAt +
", dueAt=" + dueAt +
", endAt=" + endAt +
", spentTime=" + spentTime +
", isOvertime=" + isOvertime +
", type='" + type + '\'' +
", defaultSwimlaneId='" + defaultSwimlaneId + '\'' +
'}';
}
}

View File

@ -0,0 +1,30 @@
package wekan.wrapper.entity;
import com.google.gson.annotations.SerializedName;
public enum BoardBackgroundColor {
@SerializedName("belize")
BELIZE,
@SerializedName("nephritis")
NEPHRITIS,
@SerializedName("pomegranate")
POMEGRANATE,
@SerializedName("pumpkin")
PUMPIK,
@SerializedName("wisteria")
WISTERIA,
@SerializedName("moderatepink")
MODERATEPINK,
@SerializedName("strongcyan")
STRONGCYAN,
@SerializedName("limegreen")
LIMEGREEN,
@SerializedName("midnight")
MIDNIGHT,
@SerializedName("dark")
DARK,
@SerializedName("relax")
RELAX,
@SerializedName("corteza")
CORTEZA
}

View File

@ -0,0 +1,10 @@
package wekan.wrapper.entity;
import com.google.gson.annotations.SerializedName;
public enum BoardPermission {
@SerializedName("public")
PUBLIC,
@SerializedName("private")
PRIVATE
}

View File

@ -0,0 +1,133 @@
package wekan.wrapper.entity;
import com.google.gson.annotations.SerializedName;
/**
* Describes the properties of the new board
*/
public class BoardPrototype {
private String title;
private String owner;
private boolean isAdmin;
private boolean isActive;
private boolean isNoComments;
private boolean isCommentOnly;
private BoardPermission permission;
@SerializedName("color")
private BoardBackgroundColor backgroundColor;
private BoardPrototype(
String title,
String owner,
boolean isAdmin,
boolean isActive,
boolean isNoComments,
boolean isCommentOnly,
BoardPermission permission,
BoardBackgroundColor backgroundColor
) {
this.title = title;
this.owner = owner;
this.isAdmin = isAdmin;
this.isActive = isActive;
this.isNoComments = isNoComments;
this.isCommentOnly = isCommentOnly;
this.permission = permission;
this.backgroundColor = backgroundColor;
}
public static class Builder {
private String title;
private String owner;
private boolean isActive = true;
private MemberPermission memberPermission = MemberPermission.ADMIN;
private BoardPermission boardPermission = BoardPermission.PRIVATE;
private BoardBackgroundColor backgroundColor = BoardBackgroundColor.BELIZE;
public Builder() {
}
/**
* Create a new BoardPrototype
*
* @return The board prototype
*/
public BoardPrototype build() {
return new BoardPrototype(
title,
owner,
memberPermission.isAdmin(),
isActive,
memberPermission.isNoComments(),
memberPermission.isCommentOnly(),
boardPermission,
backgroundColor
);
}
/**
* Set the title of the new board.
*
* @param title The title of the new board
*/
public Builder setTitle(String title) {
this.title = title;
return this;
}
/**
* Set the owner of the new board.
*
* @param owner The owner of the new board
*/
public Builder setOwner(String owner) {
this.owner = owner;
return this;
}
/**
* Set the owner like an active member of the new board.
*
* @param active True by default
*/
public Builder setActive(boolean active) {
isActive = active;
return this;
}
/**
* Set the permission of the owner on the new board.
*
* @param memberPermission ADMIN by default
* @see MemberPermission
*/
public Builder setMemberPermission(MemberPermission memberPermission) {
this.memberPermission = memberPermission;
return this;
}
/**
* Set the visibility of the new board.
*
* @param boardPermission PRIVATE by default
* @see BoardPermission
*/
public Builder setBoardPermission(BoardPermission boardPermission) {
this.boardPermission = boardPermission;
return this;
}
/**
* Set the background color of the new board.
*
* @param backgroundColor BELIZE by default
* @see BoardBackgroundColor
*/
public Builder setBackgroundColor(BoardBackgroundColor backgroundColor) {
this.backgroundColor = backgroundColor;
return this;
}
}
}

View File

@ -1,5 +0,0 @@
package wekan.wrapper.entity;
public class ExampleEntity {
}

View File

@ -0,0 +1,37 @@
package wekan.wrapper.entity;
import com.google.gson.annotations.SerializedName;
public class Label {
@SerializedName("_id")
private String id;
private String name;
private LabelColor color;
public Label(String id, String name, LabelColor color) {
this.id = id;
this.name = name;
this.color = color;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public LabelColor getColor() {
return color;
}
@Override
public String toString() {
return "Label{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", color=" + color +
'}';
}
}

View File

@ -0,0 +1,54 @@
package wekan.wrapper.entity;
import com.google.gson.annotations.SerializedName;
public enum LabelColor {
@SerializedName("green")
GREEN,
@SerializedName("yellow")
YELLOW,
@SerializedName("orange")
ORANGE,
@SerializedName("red")
RED,
@SerializedName("purple")
PURPLE,
@SerializedName("blue")
BLUE,
@SerializedName("sky")
SKY,
@SerializedName("lime")
LIME,
@SerializedName("pink")
PINK,
@SerializedName("black")
BLACK,
@SerializedName("silver")
SILVER,
@SerializedName("peachpuff")
PEACHPUFF,
@SerializedName("crimson")
CRIMSON,
@SerializedName("plum")
PLUM,
@SerializedName("darkgreen")
DARKGREEN,
@SerializedName("slateblue")
SLATEBLUE,
@SerializedName("magenta")
MAGENTA,
@SerializedName("gold")
GOLD,
@SerializedName("navy")
NAVY,
@SerializedName("gray")
GRAY,
@SerializedName("saddlebrown")
SADDLEBROWN,
@SerializedName("paleturquoise")
PALETURQUOISE,
@SerializedName("mistyrose")
MISTYROSE,
@SerializedName("indigo")
INDIGO
}

View File

@ -0,0 +1,16 @@
package wekan.wrapper.entity;
/**
* Describes the properties of the new label.
*/
public class LabelPrototype {
private Label label;
/**
* @param name The name of the new label
* @param color The color of the new label
*/
public LabelPrototype(String name, LabelColor color) {
label = new Label(null, name, color);
}
}

View File

@ -0,0 +1,48 @@
package wekan.wrapper.entity;
public class Member {
private String userId;
private boolean isActive;
private boolean isAdmin;
private boolean isNoComments;
private boolean isCommentOnly;
public Member(String userId, boolean isActive, boolean isAdmin, boolean isNoComments, boolean isCommentOnly) {
this.userId = userId;
this.isActive = isActive;
this.isAdmin = isAdmin;
this.isNoComments = isNoComments;
this.isCommentOnly = isCommentOnly;
}
public String getUserId() {
return userId;
}
public boolean isActive() {
return isActive;
}
public boolean isAdmin() {
return isAdmin;
}
public boolean isNoComments() {
return isNoComments;
}
public boolean isCommentOnly() {
return isCommentOnly;
}
@Override
public String toString() {
return "Member{" +
"userId='" + userId + '\'' +
", isActive=" + isActive +
", isAdmin=" + isAdmin +
", isNoComments=" + isNoComments +
", isCommentOnly=" + isCommentOnly +
'}';
}
}

View File

@ -0,0 +1,61 @@
package wekan.wrapper.entity;
public class MemberPermission {
/**
* The user is an admin of the board
*/
public final static MemberPermission ADMIN = new MemberPermission(
true,
false,
false
);
/**
* The use is an normal member of the board
*/
public final static MemberPermission NORMAL = new MemberPermission(
false,
false,
false
);
/**
* The user isn't allowed to make comments on the board
*/
public final static MemberPermission NO_COMMENTS = new MemberPermission(
false,
true,
false
);
/**
* The user is only allowed to make comments on the board
*/
public final static MemberPermission COMMENT_ONLY = new MemberPermission(
false,
false,
true
);
private boolean isAdmin;
private boolean isNoComments;
private boolean isCommentOnly;
private MemberPermission(boolean isAdmin, boolean isNoComments, boolean isCommentOnly) {
this.isAdmin = isAdmin;
this.isNoComments = isNoComments;
this.isCommentOnly = isCommentOnly;
}
public boolean isAdmin() {
return isAdmin;
}
public boolean isNoComments() {
return isNoComments;
}
public boolean isCommentOnly() {
return isCommentOnly;
}
}

View File

@ -0,0 +1,16 @@
package wekan.wrapper.entity;
import com.google.gson.annotations.SerializedName;
public enum PresentParentTask {
@SerializedName("prefix-with-full-path")
PREFIX_WITH_FULL_PATH,
@SerializedName("prefix-with-parent")
PREFIX_WITH_PARENT,
@SerializedName("subtext-with-full-path")
SUBTEXT_WITH_FULL_PATH,
@SerializedName("subtext-with-parent")
SUBTEXT_WITH_PARENT,
@SerializedName("no-parent")
NO_PARENT
}

View File

@ -0,0 +1,213 @@
package wekan.wrapper.api;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import wekan.wrapper.entity.*;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.List;
import static org.junit.Assert.*;
public class BoardServiceTest {
private MockWebServer mockWebServer = new MockWebServer();
private BoardService service = null;
@Before
public void setUp() {
try {
mockWebServer.start();
service = new Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(BoardService.class);
} catch (IOException e) {
e.printStackTrace();
}
}
@After
public void teardown() {
try {
mockWebServer.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void getPublicBoard() {
MockResponse response = new MockResponse()
.setResponseCode(HttpURLConnection.HTTP_OK)
.setBody(
"[{\"_id\":\"e7pJ3T7WciDz5P2v0\",\"title\":\"board 0\"}," +
"{\"_id\":\"e7pJ3T7WciDz5P2v1\",\"title\":\"board 1\"}]"
);
mockWebServer.enqueue(response);
try {
List<Board> boards = service.getPublicBoards().
execute().body();
assertNotNull(boards);
assertEquals(2, boards.size());
for (int i = 0; i < boards.size(); i++) {
assertEquals("e7pJ3T7WciDz5P2v" + i, boards.get(i).getId());
assertEquals("board " + i, boards.get(i).getTitle());
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void newBoardTest() {
MockResponse response = new MockResponse()
.setResponseCode(HttpURLConnection.HTTP_OK)
.setBody(
"{\"_id\":\"cYGkSRfKfEEFDQjv2\",\"defaultSwimlaneId\":\"TffifkXqDvwz9LJNs\"}"
);
mockWebServer.enqueue(response);
try {
Board board = service.newBoard(
new BoardPrototype.Builder()
.setTitle("title")
.setOwner("owner")
.build()
)
.execute().body();
assertNotNull(board);
assertEquals("cYGkSRfKfEEFDQjv2", board.getId());
assertEquals("TffifkXqDvwz9LJNs", board.getDefaultSwimlaneId());
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void getBoardTest() {
MockResponse response = new MockResponse()
.setResponseCode(HttpURLConnection.HTTP_OK)
.setBody(board1);
mockWebServer.enqueue(response);
try {
Board board = service.getBoard("id")
.execute().body();
assertNotNull(board);
assertEquals("id1", board.getId());
assertEquals("my title1", board.getTitle());
assertEquals("my-title-1", board.getSlug());
assertFalse(board.isArchived());
assertEquals(0, board.getStarts());
assertEquals(BoardPermission.PRIVATE, board.getPermission());
assertEquals(BoardBackgroundColor.CORTEZA, board.getBackgroundColor());
assertTrue(board.isAllowsSubtasks());
assertEquals(PresentParentTask.NO_PARENT, board.getPresentParentTask());
assertEquals(0, board.getSpentTime());
assertFalse(board.isOvertime());
assertEquals("board", board.getType());
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void getBoardsFromUser() {
MockResponse response = new MockResponse()
.setResponseCode(HttpURLConnection.HTTP_OK)
.setBody("[ " + board1 + ", " + board2 + ", " + board3 + " ]");
mockWebServer.enqueue(response);
try {
List<Board> boards = service.getBoardsFromUser("user-id")
.execute().body();
assertNotNull(boards);
assertEquals(3, boards.size());
} catch (IOException e) {
e.printStackTrace();
}
}
private static final String board1 =
"{" +
"\"_id\":\"id1\"," +
"\"title\":\"my title1\"," +
"\"members\":[{\"userId\":\"Si69gNgkJfQuk6uiJ\",\"isAdmin\":true,\"isActive\":true,\"isNoComments\":false,\"isCommentOnly\":false}]," +
"\"permission\":\"private\"," +
"\"color\":\"corteza\"," +
"\"slug\":\"my-title-1\"," +
"\"archived\":false," +
"\"createdAt\":\"2019-11-09T10:01:21.280Z\"," +
"\"modifiedAt\":\"2019-11-09T10:01:21.280Z\"," +
"\"stars\":0," +
"\"labels\":[{\"color\":\"green\",\"_id\":\"3NvZnG\",\"name\":\"\"},{\"color\":\"yellow\",\"_id\":\"AcBqR9\",\"name\":\"\"},{\"color\":\"orange\",\"_id\":\"JxEw9Z\",\"name\":\"\"},{\"color\":\"red\",\"_id\":\"grdRCS\",\"name\":\"\"},{\"color\":\"purple\",\"_id\":\"buMfKA\",\"name\":\"\"},{\"color\":\"blue\",\"_id\":\"sbi9FZ\",\"name\":\"\"}]," +
"\"subtasksDefaultBoardId\":null," +
"\"subtasksDefaultListId\":null," +
"\"allowsSubtasks\":true," +
"\"presentParentTask\":\"no-parent\"," +
"\"isOvertime\":false," +
"\"type\":\"board\"" +
"}";
private static final String board2 =
"{" +
"\"_id\":\"id2\"," +
"\"title\":\"my title2\"," +
"\"members\":[{\"userId\":\"Si69gNgkJfQuk6uiJ\",\"isAdmin\":true,\"isActive\":true,\"isNoComments\":false,\"isCommentOnly\":false}]," +
"\"permission\":\"private\"," +
"\"color\":\"corteza\"," +
"\"slug\":\"my-title-2\"," +
"\"archived\":false," +
"\"createdAt\":\"2019-11-09T10:01:21.280Z\"," +
"\"modifiedAt\":\"2019-11-09T10:01:21.280Z\"," +
"\"stars\":0," +
"\"labels\":[{\"color\":\"green\",\"_id\":\"3NvZnG\",\"name\":\"\"},{\"color\":\"yellow\",\"_id\":\"AcBqR9\",\"name\":\"\"},{\"color\":\"orange\",\"_id\":\"JxEw9Z\",\"name\":\"\"},{\"color\":\"red\",\"_id\":\"grdRCS\",\"name\":\"\"},{\"color\":\"purple\",\"_id\":\"buMfKA\",\"name\":\"\"},{\"color\":\"blue\",\"_id\":\"sbi9FZ\",\"name\":\"\"}]," +
"\"subtasksDefaultBoardId\":null," +
"\"subtasksDefaultListId\":null," +
"\"allowsSubtasks\":true," +
"\"presentParentTask\":\"no-parent\"," +
"\"isOvertime\":false," +
"\"type\":\"board\"" +
"}";
private static final String board3 =
"{" +
"\"_id\":\"id3\"," +
"\"title\":\"my title3\"," +
"\"members\":[{\"userId\":\"Si69gNgkJfQuk6uiJ\",\"isAdmin\":true,\"isActive\":true,\"isNoComments\":false,\"isCommentOnly\":false}]," +
"\"permission\":\"private\"," +
"\"color\":\"corteza\"," +
"\"slug\":\"my-title-3\"," +
"\"archived\":false," +
"\"createdAt\":\"2019-11-09T10:01:21.280Z\"," +
"\"modifiedAt\":\"2019-11-09T10:01:21.280Z\"," +
"\"stars\":0," +
"\"labels\":[{\"color\":\"green\",\"_id\":\"3NvZnG\",\"name\":\"\"},{\"color\":\"yellow\",\"_id\":\"AcBqR9\",\"name\":\"\"},{\"color\":\"orange\",\"_id\":\"JxEw9Z\",\"name\":\"\"},{\"color\":\"red\",\"_id\":\"grdRCS\",\"name\":\"\"},{\"color\":\"purple\",\"_id\":\"buMfKA\",\"name\":\"\"},{\"color\":\"blue\",\"_id\":\"sbi9FZ\",\"name\":\"\"}]," +
"\"subtasksDefaultBoardId\":null," +
"\"subtasksDefaultListId\":null," +
"\"allowsSubtasks\":true," +
"\"presentParentTask\":\"no-parent\"," +
"\"isOvertime\":false," +
"\"type\":\"board\"" +
"}";
}

View File

@ -1,13 +0,0 @@
package wekan.wrapper.api;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class ExampleTest {
@Test
public void test() {
assertTrue(true);
}
}