From 43a66b6521d2617c86dda9b5596c2bf4a5f3fe8c Mon Sep 17 00:00:00 2001 From: norangebit Date: Thu, 7 Nov 2019 19:29:47 +0100 Subject: [PATCH 01/13] add drone.yml --- .drone.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .drone.yml diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..1b4406f --- /dev/null +++ b/.drone.yml @@ -0,0 +1,21 @@ +kind: pipeline +type: docker +name: default + +steps: +- name: build + image: gradle:5.6.3 + commands: + - gradle build + +- name: notify + image: appleboy/drone-telegram + settings: + token: + from_secret: telegram_token + to: + from_secret: telegram_user + format: markdown + message: | + Build {{build.number}} from commit [{{truncate commit.sha 10}}]({{commit.link}}) **failed**. + {{uppercasefirst commit.author}} please fix me! From 96b66e274ad2b48f6066f680cd477fa63c149607 Mon Sep 17 00:00:00 2001 From: norangebit Date: Thu, 7 Nov 2019 19:34:59 +0100 Subject: [PATCH 02/13] edit drone.yml Send notification only on failure --- .drone.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.drone.yml b/.drone.yml index 1b4406f..f96f3b0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -19,3 +19,6 @@ steps: message: | Build {{build.number}} from commit [{{truncate commit.sha 10}}]({{commit.link}}) **failed**. {{uppercasefirst commit.author}} please fix me! + when: + status: + - failure From a72f27340c375ecd5eecebe3cf4d9e9ee4c799e8 Mon Sep 17 00:00:00 2001 From: norangebit Date: Fri, 8 Nov 2019 22:21:51 +0100 Subject: [PATCH 03/13] add MockWebServer dependence --- wrapper/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index 5efabc9..94cb0da 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -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") } From 7337e1831bf5da05fb2688c1cacd1eca04978b3c Mon Sep 17 00:00:00 2001 From: norangebit Date: Sat, 9 Nov 2019 11:43:43 +0100 Subject: [PATCH 04/13] add getPublicBoards, newBoard and getBoard - add Board entity - add BoardPrototype entity - add enum class Color - add enum class Permission - add enum class PresentParentTask --- .../java/wekan/wrapper/api/BoardService.java | 22 +++ .../java/wekan/wrapper/api/ExampleApi.java | 4 - .../main/java/wekan/wrapper/entity/Board.java | 143 +++++++++++++++++ .../wekan/wrapper/entity/BoardPrototype.java | 25 +++ .../main/java/wekan/wrapper/entity/Color.java | 30 ++++ .../wekan/wrapper/entity/ExampleEntity.java | 5 - .../java/wekan/wrapper/entity/Permission.java | 10 ++ .../wrapper/entity/PresentParentTask.java | 16 ++ .../wekan/wrapper/api/BoardServiceTest.java | 147 ++++++++++++++++++ .../wekan/wrapper/api/BoardServiceTestX.java | 51 ++++++ .../java/wekan/wrapper/api/ExampleTest.java | 13 -- 11 files changed, 444 insertions(+), 22 deletions(-) create mode 100644 wrapper/src/main/java/wekan/wrapper/api/BoardService.java delete mode 100644 wrapper/src/main/java/wekan/wrapper/api/ExampleApi.java create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/Board.java create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/Color.java delete mode 100644 wrapper/src/main/java/wekan/wrapper/entity/ExampleEntity.java create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/Permission.java create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/PresentParentTask.java create mode 100644 wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java create mode 100644 wrapper/src/test/java/wekan/wrapper/api/BoardServiceTestX.java delete mode 100644 wrapper/src/test/java/wekan/wrapper/api/ExampleTest.java diff --git a/wrapper/src/main/java/wekan/wrapper/api/BoardService.java b/wrapper/src/main/java/wekan/wrapper/api/BoardService.java new file mode 100644 index 0000000..00fc8f3 --- /dev/null +++ b/wrapper/src/main/java/wekan/wrapper/api/BoardService.java @@ -0,0 +1,22 @@ +package wekan.wrapper.api; + +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Path; +import wekan.wrapper.entity.Board; +import wekan.wrapper.entity.BoardPrototype; + +import java.util.List; + +public interface BoardService { + @GET("api/boards") + Call> getPublicBoards(); + + @POST("api/boards") + Call newBoard(@Body BoardPrototype boardPrototype); + + @GET("api/boards/{boardId}") + Call getBoard(@Path("boardId") String boardId); +} diff --git a/wrapper/src/main/java/wekan/wrapper/api/ExampleApi.java b/wrapper/src/main/java/wekan/wrapper/api/ExampleApi.java deleted file mode 100644 index 3e139bd..0000000 --- a/wrapper/src/main/java/wekan/wrapper/api/ExampleApi.java +++ /dev/null @@ -1,4 +0,0 @@ -package wekan.wrapper.api; - -public interface ExampleApi { -} diff --git a/wrapper/src/main/java/wekan/wrapper/entity/Board.java b/wrapper/src/main/java/wekan/wrapper/entity/Board.java new file mode 100644 index 0000000..10beb0a --- /dev/null +++ b/wrapper/src/main/java/wekan/wrapper/entity/Board.java @@ -0,0 +1,143 @@ +package wekan.wrapper.entity; + +import com.google.gson.annotations.SerializedName; + +import java.util.Date; + +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; + // TODO list of labels + // TODO list of members + private Permission permission; + private Color color; + 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 Permission getPermission() { + return permission; + } + + public Color getColor() { + return color; + } + + 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 + + ", permission=" + permission + + ", color=" + color + + ", description='" + description + '\'' + + ", subtasksDefaultBoardId='" + subtasksDefaultBoardId + '\'' + + ", subtasksDefaultListId='" + subtasksDefaultListId + '\'' + + ", allowsSubtasks=" + allowsSubtasks + + ", presentParentTask='" + presentParentTask + '\'' + + ", startAt=" + startAt + + ", dueAt=" + dueAt + + ", endAt=" + endAt + + ", spentTime=" + spentTime + + ", isOvertime=" + isOvertime + + ", type='" + type + '\'' + + ", defaultSwimlaneId='" + defaultSwimlaneId + '\'' + + '}'; + } +} diff --git a/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java b/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java new file mode 100644 index 0000000..cea3df6 --- /dev/null +++ b/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java @@ -0,0 +1,25 @@ +package wekan.wrapper.entity; + +public class BoardPrototype { + private String title; + private String owner; + private boolean isAdmin = true; + private boolean isActive = true; + private boolean isNoComments = false; + private boolean isCommentOnly = false; + private Permission permission = Permission.PRIVATE; + private Color color = Color.BELIZE; + + public BoardPrototype(String title, String owner) { + this.title = title; + this.owner = owner; + } + + public BoardPrototype(String title, String owner, Color color) { + this.title = title; + this.owner = owner; + this.color = color; + } +} + + diff --git a/wrapper/src/main/java/wekan/wrapper/entity/Color.java b/wrapper/src/main/java/wekan/wrapper/entity/Color.java new file mode 100644 index 0000000..c211fb8 --- /dev/null +++ b/wrapper/src/main/java/wekan/wrapper/entity/Color.java @@ -0,0 +1,30 @@ +package wekan.wrapper.entity; + +import com.google.gson.annotations.SerializedName; + +public enum Color { + @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 +} diff --git a/wrapper/src/main/java/wekan/wrapper/entity/ExampleEntity.java b/wrapper/src/main/java/wekan/wrapper/entity/ExampleEntity.java deleted file mode 100644 index 3fc4073..0000000 --- a/wrapper/src/main/java/wekan/wrapper/entity/ExampleEntity.java +++ /dev/null @@ -1,5 +0,0 @@ -package wekan.wrapper.entity; - -public class ExampleEntity { - -} diff --git a/wrapper/src/main/java/wekan/wrapper/entity/Permission.java b/wrapper/src/main/java/wekan/wrapper/entity/Permission.java new file mode 100644 index 0000000..7cccaf0 --- /dev/null +++ b/wrapper/src/main/java/wekan/wrapper/entity/Permission.java @@ -0,0 +1,10 @@ +package wekan.wrapper.entity; + +import com.google.gson.annotations.SerializedName; + +public enum Permission { + @SerializedName("public") + PUBLIC, + @SerializedName("private") + PRIVATE +} diff --git a/wrapper/src/main/java/wekan/wrapper/entity/PresentParentTask.java b/wrapper/src/main/java/wekan/wrapper/entity/PresentParentTask.java new file mode 100644 index 0000000..064fde3 --- /dev/null +++ b/wrapper/src/main/java/wekan/wrapper/entity/PresentParentTask.java @@ -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 +} diff --git a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java new file mode 100644 index 0000000..0f1d03c --- /dev/null +++ b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java @@ -0,0 +1,147 @@ +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 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("title", "owner")) + .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( + "{" + + "\"_id\":\"id\"," + + "\"title\":\"my title\"," + + "\"members\":[{\"userId\":\"Si69gNgkJfQuk6uiJ\",\"isAdmin\":true,\"isActive\":true,\"isNoComments\":false,\"isCommentOnly\":false}]," + + "\"permission\":\"private\"," + + "\"color\":\"corteza\"," + + "\"slug\":\"my-title\"," + + "\"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\"" + + "}" + ); + + mockWebServer.enqueue(response); + + try { + Board board = service.getBoard("id") + .execute().body(); + + assertNotNull(board); + assertEquals("id", board.getId()); + assertEquals("my title", board.getTitle()); + assertEquals("my-title", board.getSlug()); + assertFalse(board.isArchived()); + assertEquals(0, board.getStarts()); + assertEquals(Permission.PRIVATE, board.getPermission()); + assertEquals(Color.CORTEZA, board.getColor()); + 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(); + } + } +} diff --git a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTestX.java b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTestX.java new file mode 100644 index 0000000..1683ebc --- /dev/null +++ b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTestX.java @@ -0,0 +1,51 @@ +package wekan.wrapper.api; + +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.Test; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; +import wekan.wrapper.entity.Board; + +import java.io.IOException; +import java.util.List; + +public class BoardServiceTestX { + + @Test + public void test() { + + OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); + + httpClient.addInterceptor(new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request().newBuilder() + .addHeader("Authorization", "Bearer GC0ibjF-5OlYczYe6WKCXX_72MtXVC-OEUK4puf9VeI") + .addHeader("Accept", "application/json") + .build(); + return chain.proceed(request); + } + }); + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl("https://board.norangeb.it") + .addConverterFactory(GsonConverterFactory.create()) + .client(httpClient.build()) + .build(); + + BoardService boardService = retrofit.create(BoardService.class); + + try { + List list = boardService.getPublicBoard().execute().body(); + for (Board b : list){ + System.out.println(b.getId()); + System.out.println(b.getTitle()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/wrapper/src/test/java/wekan/wrapper/api/ExampleTest.java b/wrapper/src/test/java/wekan/wrapper/api/ExampleTest.java deleted file mode 100644 index a6a25b8..0000000 --- a/wrapper/src/test/java/wekan/wrapper/api/ExampleTest.java +++ /dev/null @@ -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); - } -} From fda9488120cf5813b8cd2b7638c6cfeb5858b3de Mon Sep 17 00:00:00 2001 From: norangebit Date: Sat, 9 Nov 2019 11:53:25 +0100 Subject: [PATCH 05/13] remove old test --- .../wekan/wrapper/api/BoardServiceTestX.java | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 wrapper/src/test/java/wekan/wrapper/api/BoardServiceTestX.java diff --git a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTestX.java b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTestX.java deleted file mode 100644 index 1683ebc..0000000 --- a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTestX.java +++ /dev/null @@ -1,51 +0,0 @@ -package wekan.wrapper.api; - -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.junit.Test; -import retrofit2.Retrofit; -import retrofit2.converter.gson.GsonConverterFactory; -import wekan.wrapper.entity.Board; - -import java.io.IOException; -import java.util.List; - -public class BoardServiceTestX { - - @Test - public void test() { - - OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); - - httpClient.addInterceptor(new Interceptor() { - @Override - public Response intercept(Chain chain) throws IOException { - Request request = chain.request().newBuilder() - .addHeader("Authorization", "Bearer GC0ibjF-5OlYczYe6WKCXX_72MtXVC-OEUK4puf9VeI") - .addHeader("Accept", "application/json") - .build(); - return chain.proceed(request); - } - }); - - Retrofit retrofit = new Retrofit.Builder() - .baseUrl("https://board.norangeb.it") - .addConverterFactory(GsonConverterFactory.create()) - .client(httpClient.build()) - .build(); - - BoardService boardService = retrofit.create(BoardService.class); - - try { - List list = boardService.getPublicBoard().execute().body(); - for (Board b : list){ - System.out.println(b.getId()); - System.out.println(b.getTitle()); - } - } catch (IOException e) { - e.printStackTrace(); - } - } -} From 9ea7914e9198d099b4e56d6f1c95f595face5946 Mon Sep 17 00:00:00 2001 From: norangebit Date: Sat, 9 Nov 2019 14:13:52 +0100 Subject: [PATCH 06/13] add BoardPrototypeBuilder --- .../wekan/wrapper/entity/BoardPrototype.java | 98 ++++++++++++++++--- .../wekan/wrapper/api/BoardServiceTest.java | 45 +++++---- 2 files changed, 112 insertions(+), 31 deletions(-) diff --git a/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java b/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java index cea3df6..4766382 100644 --- a/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java +++ b/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java @@ -3,22 +3,98 @@ package wekan.wrapper.entity; public class BoardPrototype { private String title; private String owner; - private boolean isAdmin = true; - private boolean isActive = true; - private boolean isNoComments = false; - private boolean isCommentOnly = false; - private Permission permission = Permission.PRIVATE; - private Color color = Color.BELIZE; + private boolean isAdmin; + private boolean isActive; + private boolean isNoComments; + private boolean isCommentOnly; + private Permission permission; + private Color color; - public BoardPrototype(String title, String owner) { + private BoardPrototype( + String title, + String owner, + boolean isAdmin, + boolean isActive, + boolean isNoComments, + boolean isCommentOnly, + Permission permission, + Color color + ) { this.title = title; this.owner = owner; + this.isAdmin = isAdmin; + this.isActive = isActive; + this.isNoComments = isNoComments; + this.isCommentOnly = isCommentOnly; + this.permission = permission; + this.color = color; } - public BoardPrototype(String title, String owner, Color color) { - this.title = title; - this.owner = owner; - this.color = color; + public static class Builder { + private String title; + private String owner; + private boolean isAdmin = true; + private boolean isActive = true; + private boolean isNoComments = false; + private boolean isCommentOnly = false; + private Permission permission = Permission.PRIVATE; + private Color color = Color.BELIZE; + + public Builder() { + } + + public BoardPrototype build() { + return new BoardPrototype( + title, + owner, + isAdmin, + isActive, + isNoComments, + isCommentOnly, + permission, + color + ); + } + + public Builder setTitle(String title) { + this.title = title; + return this; + } + + public Builder setOwner(String owner) { + this.owner = owner; + return this; + } + + public Builder setAdmin(boolean admin) { + isAdmin = admin; + return this; + } + + public Builder setActive(boolean active) { + isActive = active; + return this; + } + + public Builder setNoComments(boolean noComments) { + isNoComments = noComments; + return this; + } + + public Builder setCommentOnly(boolean commentOnly) { + isCommentOnly = commentOnly; + return this; + } + + public Builder setPermission(Permission permission) { + this.permission = permission; + return this; + } + + public Builder setColor(Color color) { + this.color = color; + return this; + } } } diff --git a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java index 0f1d03c..bcb8a95 100644 --- a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java +++ b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java @@ -82,7 +82,12 @@ public class BoardServiceTest { mockWebServer.enqueue(response); try { - Board board = service.newBoard(new BoardPrototype("title", "owner")) + Board board = service.newBoard( + new BoardPrototype.Builder() + .setTitle("title") + .setOwner("owner") + .build() + ) .execute().body(); assertNotNull(board); @@ -99,25 +104,25 @@ public class BoardServiceTest { MockResponse response = new MockResponse() .setResponseCode(HttpURLConnection.HTTP_OK) .setBody( - "{" + - "\"_id\":\"id\"," + - "\"title\":\"my title\"," + - "\"members\":[{\"userId\":\"Si69gNgkJfQuk6uiJ\",\"isAdmin\":true,\"isActive\":true,\"isNoComments\":false,\"isCommentOnly\":false}]," + - "\"permission\":\"private\"," + - "\"color\":\"corteza\"," + - "\"slug\":\"my-title\"," + - "\"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\"" + - "}" + "{" + + "\"_id\":\"id\"," + + "\"title\":\"my title\"," + + "\"members\":[{\"userId\":\"Si69gNgkJfQuk6uiJ\",\"isAdmin\":true,\"isActive\":true,\"isNoComments\":false,\"isCommentOnly\":false}]," + + "\"permission\":\"private\"," + + "\"color\":\"corteza\"," + + "\"slug\":\"my-title\"," + + "\"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\"" + + "}" ); mockWebServer.enqueue(response); From f4df78dc73e202a132618a4d0a2e84a895988462 Mon Sep 17 00:00:00 2001 From: norangebit Date: Sat, 9 Nov 2019 14:39:30 +0100 Subject: [PATCH 07/13] add deleteBoard and getBoardsFromUser --- .../java/wekan/wrapper/api/BoardService.java | 11 +- .../wekan/wrapper/api/BoardServiceTest.java | 109 ++++++++++++++---- 2 files changed, 92 insertions(+), 28 deletions(-) diff --git a/wrapper/src/main/java/wekan/wrapper/api/BoardService.java b/wrapper/src/main/java/wekan/wrapper/api/BoardService.java index 00fc8f3..5670561 100644 --- a/wrapper/src/main/java/wekan/wrapper/api/BoardService.java +++ b/wrapper/src/main/java/wekan/wrapper/api/BoardService.java @@ -1,10 +1,7 @@ package wekan.wrapper.api; import retrofit2.Call; -import retrofit2.http.Body; -import retrofit2.http.GET; -import retrofit2.http.POST; -import retrofit2.http.Path; +import retrofit2.http.*; import wekan.wrapper.entity.Board; import wekan.wrapper.entity.BoardPrototype; @@ -19,4 +16,10 @@ public interface BoardService { @GET("api/boards/{boardId}") Call getBoard(@Path("boardId") String boardId); + + @DELETE("api/boards/{boardId}") + Call deleteBoard(@Path("boardId") String boardId); + + @GET("api/users/{userId}/boards") + Call> getBoardsFromUser(@Path("userId") String userId); } diff --git a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java index bcb8a95..5ea31c7 100644 --- a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java +++ b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java @@ -103,27 +103,7 @@ public class BoardServiceTest { public void getBoardTest() { MockResponse response = new MockResponse() .setResponseCode(HttpURLConnection.HTTP_OK) - .setBody( - "{" + - "\"_id\":\"id\"," + - "\"title\":\"my title\"," + - "\"members\":[{\"userId\":\"Si69gNgkJfQuk6uiJ\",\"isAdmin\":true,\"isActive\":true,\"isNoComments\":false,\"isCommentOnly\":false}]," + - "\"permission\":\"private\"," + - "\"color\":\"corteza\"," + - "\"slug\":\"my-title\"," + - "\"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\"" + - "}" - ); + .setBody(board1); mockWebServer.enqueue(response); @@ -132,9 +112,9 @@ public class BoardServiceTest { .execute().body(); assertNotNull(board); - assertEquals("id", board.getId()); - assertEquals("my title", board.getTitle()); - assertEquals("my-title", board.getSlug()); + assertEquals("id1", board.getId()); + assertEquals("my title1", board.getTitle()); + assertEquals("my-title-1", board.getSlug()); assertFalse(board.isArchived()); assertEquals(0, board.getStarts()); assertEquals(Permission.PRIVATE, board.getPermission()); @@ -149,4 +129,85 @@ public class BoardServiceTest { e.printStackTrace(); } } + + @Test + public void getBoardsFromUser() { + MockResponse response = new MockResponse() + .setResponseCode(HttpURLConnection.HTTP_OK) + .setBody("[ " + board1 + ", " + board2 + ", " + board3 + " ]"); + + mockWebServer.enqueue(response); + + try { + List 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\"" + + "}"; } From 991b19a1b8cb1882cb4c1e7fead6eddf018f2729 Mon Sep 17 00:00:00 2001 From: norangebit Date: Sat, 9 Nov 2019 23:57:15 +0100 Subject: [PATCH 08/13] add SetBoardMemberPermission --- .../java/wekan/wrapper/api/BoardService.java | 8 +++++ .../wrapper/entity/MemberPermission.java | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/MemberPermission.java diff --git a/wrapper/src/main/java/wekan/wrapper/api/BoardService.java b/wrapper/src/main/java/wekan/wrapper/api/BoardService.java index 5670561..3842d2f 100644 --- a/wrapper/src/main/java/wekan/wrapper/api/BoardService.java +++ b/wrapper/src/main/java/wekan/wrapper/api/BoardService.java @@ -3,6 +3,7 @@ package wekan.wrapper.api; import retrofit2.Call; import retrofit2.http.*; import wekan.wrapper.entity.Board; +import wekan.wrapper.entity.MemberPermission; import wekan.wrapper.entity.BoardPrototype; import java.util.List; @@ -20,6 +21,13 @@ public interface BoardService { @DELETE("api/boards/{boardId}") Call deleteBoard(@Path("boardId") String boardId); + @POST("/api/boards/{boardId}/members/{memberId}") + Call setBoardMemberPermission( + @Path("boardId") String board, + @Path("memberId") String member, + @Body MemberPermission memberPermission + ); + @GET("api/users/{userId}/boards") Call> getBoardsFromUser(@Path("userId") String userId); } diff --git a/wrapper/src/main/java/wekan/wrapper/entity/MemberPermission.java b/wrapper/src/main/java/wekan/wrapper/entity/MemberPermission.java new file mode 100644 index 0000000..d321ec1 --- /dev/null +++ b/wrapper/src/main/java/wekan/wrapper/entity/MemberPermission.java @@ -0,0 +1,34 @@ +package wekan.wrapper.entity; + +public class MemberPermission { + public final static MemberPermission ADMIN = new MemberPermission( + true, + false, + false + ); + public final static MemberPermission NORMAL = new MemberPermission( + false, + false, + false + ); + public final static MemberPermission NO_COMMENTS = new MemberPermission( + false, + true, + false + ); + 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; + } +} From f4d51da4431d74183dc8fa6f89ac5c0995d175fa Mon Sep 17 00:00:00 2001 From: norangebit Date: Sun, 10 Nov 2019 10:51:43 +0100 Subject: [PATCH 09/13] refactoring - change Color to BoardBackgroundColor - change setBoardMemberPermission to setMemberPermission --- .../java/wekan/wrapper/api/BoardService.java | 2 +- .../main/java/wekan/wrapper/entity/Board.java | 9 +++++---- .../{Color.java => BoardBackgroundColor.java} | 2 +- .../wekan/wrapper/entity/BoardPrototype.java | 17 ++++++++++------- .../wekan/wrapper/api/BoardServiceTest.java | 2 +- 5 files changed, 18 insertions(+), 14 deletions(-) rename wrapper/src/main/java/wekan/wrapper/entity/{Color.java => BoardBackgroundColor.java} (94%) diff --git a/wrapper/src/main/java/wekan/wrapper/api/BoardService.java b/wrapper/src/main/java/wekan/wrapper/api/BoardService.java index 3842d2f..4f84e4d 100644 --- a/wrapper/src/main/java/wekan/wrapper/api/BoardService.java +++ b/wrapper/src/main/java/wekan/wrapper/api/BoardService.java @@ -22,7 +22,7 @@ public interface BoardService { Call deleteBoard(@Path("boardId") String boardId); @POST("/api/boards/{boardId}/members/{memberId}") - Call setBoardMemberPermission( + Call setMemberPermission( @Path("boardId") String board, @Path("memberId") String member, @Body MemberPermission memberPermission diff --git a/wrapper/src/main/java/wekan/wrapper/entity/Board.java b/wrapper/src/main/java/wekan/wrapper/entity/Board.java index 10beb0a..a082b4f 100644 --- a/wrapper/src/main/java/wekan/wrapper/entity/Board.java +++ b/wrapper/src/main/java/wekan/wrapper/entity/Board.java @@ -16,7 +16,8 @@ public class Board { // TODO list of labels // TODO list of members private Permission permission; - private Color color; + @SerializedName("color") + private BoardBackgroundColor backgroundColor; private String description; private String subtasksDefaultBoardId; private String subtasksDefaultListId; @@ -62,8 +63,8 @@ public class Board { return permission; } - public Color getColor() { - return color; + public BoardBackgroundColor getBackgroundColor() { + return backgroundColor; } public String getDescription() { @@ -125,7 +126,7 @@ public class Board { ", modifiedAt=" + modifiedAt + ", starts=" + starts + ", permission=" + permission + - ", color=" + color + + ", color=" + backgroundColor + ", description='" + description + '\'' + ", subtasksDefaultBoardId='" + subtasksDefaultBoardId + '\'' + ", subtasksDefaultListId='" + subtasksDefaultListId + '\'' + diff --git a/wrapper/src/main/java/wekan/wrapper/entity/Color.java b/wrapper/src/main/java/wekan/wrapper/entity/BoardBackgroundColor.java similarity index 94% rename from wrapper/src/main/java/wekan/wrapper/entity/Color.java rename to wrapper/src/main/java/wekan/wrapper/entity/BoardBackgroundColor.java index c211fb8..cc71175 100644 --- a/wrapper/src/main/java/wekan/wrapper/entity/Color.java +++ b/wrapper/src/main/java/wekan/wrapper/entity/BoardBackgroundColor.java @@ -2,7 +2,7 @@ package wekan.wrapper.entity; import com.google.gson.annotations.SerializedName; -public enum Color { +public enum BoardBackgroundColor { @SerializedName("belize") BELIZE, @SerializedName("nephritis") diff --git a/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java b/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java index 4766382..97afb56 100644 --- a/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java +++ b/wrapper/src/main/java/wekan/wrapper/entity/BoardPrototype.java @@ -1,5 +1,7 @@ package wekan.wrapper.entity; +import com.google.gson.annotations.SerializedName; + public class BoardPrototype { private String title; private String owner; @@ -8,7 +10,8 @@ public class BoardPrototype { private boolean isNoComments; private boolean isCommentOnly; private Permission permission; - private Color color; + @SerializedName("color") + private BoardBackgroundColor backgroundColor; private BoardPrototype( String title, @@ -18,7 +21,7 @@ public class BoardPrototype { boolean isNoComments, boolean isCommentOnly, Permission permission, - Color color + BoardBackgroundColor backgroundColor ) { this.title = title; this.owner = owner; @@ -27,7 +30,7 @@ public class BoardPrototype { this.isNoComments = isNoComments; this.isCommentOnly = isCommentOnly; this.permission = permission; - this.color = color; + this.backgroundColor = backgroundColor; } public static class Builder { @@ -38,7 +41,7 @@ public class BoardPrototype { private boolean isNoComments = false; private boolean isCommentOnly = false; private Permission permission = Permission.PRIVATE; - private Color color = Color.BELIZE; + private BoardBackgroundColor backgroundColor = BoardBackgroundColor.BELIZE; public Builder() { } @@ -52,7 +55,7 @@ public class BoardPrototype { isNoComments, isCommentOnly, permission, - color + backgroundColor ); } @@ -91,8 +94,8 @@ public class BoardPrototype { return this; } - public Builder setColor(Color color) { - this.color = color; + public Builder setBackgroundColor(BoardBackgroundColor backgroundColor) { + this.backgroundColor = backgroundColor; return this; } } diff --git a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java index 5ea31c7..78d57fc 100644 --- a/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java +++ b/wrapper/src/test/java/wekan/wrapper/api/BoardServiceTest.java @@ -118,7 +118,7 @@ public class BoardServiceTest { assertFalse(board.isArchived()); assertEquals(0, board.getStarts()); assertEquals(Permission.PRIVATE, board.getPermission()); - assertEquals(Color.CORTEZA, board.getColor()); + assertEquals(BoardBackgroundColor.CORTEZA, board.getBackgroundColor()); assertTrue(board.isAllowsSubtasks()); assertEquals(PresentParentTask.NO_PARENT, board.getPresentParentTask()); assertEquals(0, board.getSpentTime()); From af7334e42ad216f0b05d17bc3cd6d08695693d4e Mon Sep 17 00:00:00 2001 From: norangebit Date: Sun, 10 Nov 2019 11:16:40 +0100 Subject: [PATCH 10/13] add Member and Label - add field labels to Board - add field members to Board - add enum LabelColor --- .../main/java/wekan/wrapper/entity/Board.java | 19 +++++-- .../main/java/wekan/wrapper/entity/Label.java | 25 +++++++++ .../java/wekan/wrapper/entity/LabelColor.java | 54 +++++++++++++++++++ .../java/wekan/wrapper/entity/Member.java | 28 ++++++++++ 4 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/Label.java create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/LabelColor.java create mode 100644 wrapper/src/main/java/wekan/wrapper/entity/Member.java diff --git a/wrapper/src/main/java/wekan/wrapper/entity/Board.java b/wrapper/src/main/java/wekan/wrapper/entity/Board.java index a082b4f..96a9eea 100644 --- a/wrapper/src/main/java/wekan/wrapper/entity/Board.java +++ b/wrapper/src/main/java/wekan/wrapper/entity/Board.java @@ -3,6 +3,7 @@ package wekan.wrapper.entity; import com.google.gson.annotations.SerializedName; import java.util.Date; +import java.util.List; public class Board { @SerializedName("_id") @@ -13,8 +14,8 @@ public class Board { private Date createdAt; private Date modifiedAt; private int starts; - // TODO list of labels - // TODO list of members + private List