From 378b9e0e49d7826b61f1bc87860137b211a66d2f Mon Sep 17 00:00:00 2001 From: Benedict Homuth Date: Sun, 21 Jul 2024 11:14:46 +0200 Subject: [PATCH 1/7] added sql migration files --- .../src/database/migrations/sql/19_add_column_subtitles.down.sql | 1 + .../src/database/migrations/sql/19_add_column_subtitles.up.sql | 1 + 2 files changed, 2 insertions(+) create mode 100644 server/src/database/migrations/sql/19_add_column_subtitles.down.sql create mode 100644 server/src/database/migrations/sql/19_add_column_subtitles.up.sql diff --git a/server/src/database/migrations/sql/19_add_column_subtitles.down.sql b/server/src/database/migrations/sql/19_add_column_subtitles.down.sql new file mode 100644 index 0000000000..74a88bec0a --- /dev/null +++ b/server/src/database/migrations/sql/19_add_column_subtitles.down.sql @@ -0,0 +1 @@ +ALTER TABLE IF EXISTS columns DROP COLUMN IF EXISTS "description"; \ No newline at end of file diff --git a/server/src/database/migrations/sql/19_add_column_subtitles.up.sql b/server/src/database/migrations/sql/19_add_column_subtitles.up.sql new file mode 100644 index 0000000000..7ac7b3a5ec --- /dev/null +++ b/server/src/database/migrations/sql/19_add_column_subtitles.up.sql @@ -0,0 +1 @@ +ALTER TABLE IF EXISTS columns ADD COLUMN "description" VARCHAR(128) DEFAULT ''; \ No newline at end of file From e65194202a703d2e2c8eb6b8aedaae0d19293787 Mon Sep 17 00:00:00 2001 From: Benedict Homuth Date: Sun, 21 Jul 2024 11:16:20 +0200 Subject: [PATCH 2/7] added description to go code --- server/src/common/dto/columns.go | 13 ++++++++++++- server/src/database/columns.go | 3 +++ server/src/services/boards/columns.go | 4 ++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/server/src/common/dto/columns.go b/server/src/common/dto/columns.go index 1ca0ed102b..10c1ed8531 100644 --- a/server/src/common/dto/columns.go +++ b/server/src/common/dto/columns.go @@ -1,8 +1,9 @@ package dto import ( - "github.com/google/uuid" "net/http" + + "github.com/google/uuid" "scrumlr.io/server/database" "scrumlr.io/server/database/types" ) @@ -16,6 +17,9 @@ type Column struct { // The column name. Name string `json:"name"` + // The column description. + Description string `json:"description"` + // The column color. Color types.Color `json:"color"` @@ -29,6 +33,7 @@ type Column struct { func (c *Column) From(column database.Column) *Column { c.ID = column.ID c.Name = column.Name + c.Description = column.Description c.Color = column.Color c.Visible = column.Visible c.Index = column.Index @@ -57,6 +62,9 @@ type ColumnRequest struct { // The column name to set. Name string `json:"name"` + // The column description to set. + Description string `json:"description"` + // The column color to set. Color types.Color `json:"color"` @@ -78,6 +86,9 @@ type ColumnUpdateRequest struct { // The column name to set. Name string `json:"name"` + // The column description to set. + Description string `json:"description"` + // The column color to set. Color types.Color `json:"color"` diff --git a/server/src/database/columns.go b/server/src/database/columns.go index b0c486fc17..d8b93accbb 100644 --- a/server/src/database/columns.go +++ b/server/src/database/columns.go @@ -18,6 +18,7 @@ type Column struct { ID uuid.UUID Board uuid.UUID Name string + Description string Color types.Color Visible bool Index int @@ -28,6 +29,7 @@ type ColumnInsert struct { bun.BaseModel `bun:"table:columns"` Board uuid.UUID Name string + Description string Color types.Color Visible *bool Index *int @@ -39,6 +41,7 @@ type ColumnUpdate struct { ID uuid.UUID Board uuid.UUID Name string + Description string Color types.Color Visible bool Index int diff --git a/server/src/services/boards/columns.go b/server/src/services/boards/columns.go index 6401b6cefd..8f01eafbd7 100644 --- a/server/src/services/boards/columns.go +++ b/server/src/services/boards/columns.go @@ -16,7 +16,7 @@ import ( ) func (s *BoardService) CreateColumn(_ context.Context, body dto.ColumnRequest) (*dto.Column, error) { - column, err := s.database.CreateColumn(database.ColumnInsert{Board: body.Board, Name: body.Name, Color: body.Color, Visible: body.Visible, Index: body.Index}) + column, err := s.database.CreateColumn(database.ColumnInsert{Board: body.Board, Name: body.Name, Description: body.Description, Color: body.Color, Visible: body.Visible, Index: body.Index}) if err != nil { logger.Get().Errorw("unable to create column", "err", err) return nil, err @@ -36,7 +36,7 @@ func (s *BoardService) DeleteColumn(_ context.Context, board, column, user uuid. } func (s *BoardService) UpdateColumn(_ context.Context, body dto.ColumnUpdateRequest) (*dto.Column, error) { - column, err := s.database.UpdateColumn(database.ColumnUpdate{ID: body.ID, Board: body.Board, Name: body.Name, Color: body.Color, Visible: body.Visible, Index: body.Index}) + column, err := s.database.UpdateColumn(database.ColumnUpdate{ID: body.ID, Board: body.Board, Name: body.Name, Description: body.Description, Color: body.Color, Visible: body.Visible, Index: body.Index}) if err != nil { logger.Get().Errorw("unable to update column", "err", err) return nil, err From 2cb398b3f63e6632be9d3d508eb2722fa8a39b6d Mon Sep 17 00:00:00 2001 From: Benedict Homuth Date: Sun, 21 Jul 2024 11:32:33 +0200 Subject: [PATCH 3/7] updated postman collection, already adapting the es6 changes --- server/api.postman_collection.json | 75 +++++++++++++++++------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/server/api.postman_collection.json b/server/api.postman_collection.json index ab92bea2eb..9fb77e07f5 100644 --- a/server/api.postman_collection.json +++ b/server/api.postman_collection.json @@ -1,10 +1,10 @@ { "info": { - "_postman_id": "45d5f690-f459-4371-a09f-8aff874591f2", + "_postman_id": "42d60e2e-95bb-4336-8893-afed92d835c3", "name": "scrumlr.io", "description": "This is the documentation for the REST API server of the application [scrumlr.io](https://scrumlr.io). You get in touch with us and send an email to [info@scrumlr.io](https://info@scrumlr.io). The software is [MIT licensed](https://opensource.org/licenses/MIT) so do whatever you want with it. If you want to checkout the progress of our development and take a peek into our backlog you can checkout our [GitHub repository](https://github.com/inovex/scrumlr.io). By the way, this already the third iteration of our server and we're still working on the interface and on further improvements. Since the API is mainly intended for our web client we won't start with API versions at the moment so breaking changes may be incoming. Once it got stable we'll maybe start with that.\n\nIf you're using the postman collection in order to explore the different resources you should also checkout the variables of the collection. Anytime you'll create new resources (e.g. your login or a board) variables will be stored and used for subsequent calls on other resources.\n\nAccess to protected resources will be authorized if a bearer token is sent or it is included in the `jwt` Cookie, which will be automatically set upon login.\n\n## Getting started\n\nLet's try to explain the basic flow of how a new board can will be created and someone tries to join the board as a participant.\n\nFirst you can check whether you are already logged in by a `GET` request on `/user`. See the _User_ section for more information.\n\n1. A user signs into the application (see _Login_ section)\n2. The user creates a new board (`POST` on `/boards`, checkout _Boards_ section)\n3. Another logged in user tries to join the board (`POST` on `/boards/{id}/participants`, checkout _Participants_ section)\n 1. If the boards access policy is set to `PUBLIC` the participant will be added to the board and afterwards all resources will be available\n 2. If the board requires a passphrase and the access policy is set to `BY_PASSPHRASE` a client error will be reported until the user sends the correct passphrase within the payload of the request\n 3. If the boards access policy is set to `BY_INVITE` a session request will be created instead and the user will be redirected to the new resource. The board owner now needs to accept or reject the request until the user can continue\n\nThese are just the basic steps of how sessions can be created. You can also have a look into the section _Realtime_ to see how you can open websockets and listen to live updates on the data.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "32837949" + "_exporter_id": "32423964" }, "item": [ { @@ -1392,7 +1392,7 @@ " pm.expect(response.code).to.be.equal(201);", " });", " ", - " res = response.json();", + " const res = response.json();", " pm.test(\"PreRequestScript – Check id is included\", function () {", " var id = res.id;", " pm.expect(id).to.exist;", @@ -2207,7 +2207,7 @@ " pm.expect(response.code).to.be.equal(200);", " });", " ", - " res = response.json();", + " const res = response.json();", " pm.test(\"PreRequestScript – Check id\", function () {", " var id = res.id;", " pm.expect(id).to.exist;", @@ -2589,26 +2589,29 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Successful POST request\", function () {\r", + "const res = pm.response.json();\r", + "\r", + "pm.test(\"Successful POST request\", () => {\r", " pm.expect(pm.response).to.have.status(201);\r", "});\r", "\r", - "pm.test(\"Check id is included\", function () {\r", - " var id = pm.response.json().id;\r", + "pm.test(\"Check id is included\", () => {\r", + " pm.expect(res.id).to.exist;\r", + " pm.expect(res.id).to.be.not.empty;\r", + " pm.collectionVariables.set(\"column_id\", res.id);\r", + "})\r", "\r", - " pm.expect(id).to.exist;\r", - " pm.expect(id).to.be.not.empty;\r", - " pm.collectionVariables.set(\"column_id\", id);\r", + "pm.test(\"Check column name to be right\", () => {\r", + " pm.expect(res.name).to.equal(\"My new column\");\r", "})\r", "\r", - "pm.test(\"Check column name to be right\", function () {\r", - " var name = pm.response.json().name;\r", - " pm.expect(name).to.equal(\"My new column\");\r", + "pm.test(\"Check if column is correctly not visible\", () => {\r", + " pm.expect(res.visible).to.eql(false);\r", "})\r", "\r", - "pm.test(\"Check if column is correctly not visible\", function () {\r", - " var visible = pm.response.json().visible;\r", - " pm.expect(visible).to.eql(false);\r", + "pm.test(\"Check if description is saved\", () => {\r", + " pm.expect(res.description).to.exist;\r", + " pm.expect(res.description).to.equal(\"The column description\");\r", "})" ], "type": "text/javascript", @@ -2621,7 +2624,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"name\": \"My new column\",\r\n \"visible\": false,\r\n \"color\": \"backlog-blue\",\r\n \"index\": 1\r\n}", + "raw": "{\r\n \"name\": \"My new column\",\r\n \"description\": \"The column description\",\r\n \"visible\": false,\r\n \"color\": \"backlog-blue\",\r\n \"index\": 1\r\n}", "options": { "raw": { "language": "json" @@ -2692,7 +2695,7 @@ } ], "cookie": [], - "body": "{\n \"id\": \"fecb346a-9d9e-43ca-b873-8fcb09f2b0c3\",\n \"name\": \"My new column\",\n \"color\": \"lean-lilac\",\n \"visible\": false,\n \"index\": 1\n}" + "body": "{\n \"id\": \"fecb346a-9d9e-43ca-b873-8fcb09f2b0c3\",\n \"name\": \"My new column\",\n \"description\": \"The column description\",\n \"color\": \"lean-lilac\",\n \"visible\": false,\n \"index\": 1\n}" } ] }, @@ -2703,18 +2706,20 @@ "listen": "test", "script": { "exec": [ + "const res = pm.response.json();", + "", "pm.test(\"Successful GET request\", function () {", " pm.expect(pm.response).to.have.status(200);", "});", "", "pm.test(\"Check length of coumns array\", function () {", - " var res = pm.response.json();", " pm.expect(res).to.be.an(\"array\");", " pm.expect(res.length).to.be.equal(3);", "})", "" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -2771,7 +2776,7 @@ } ], "cookie": [], - "body": "[\n {\n \"id\": \"526d5efb-281d-4f4f-b308-6537e6727381\",\n \"name\": \"Lean coffee\",\n \"color\": \"backlog-blue\",\n \"visible\": true,\n \"index\": 0\n },\n {\n \"id\": \"fecb346a-9d9e-43ca-b873-8fcb09f2b0c3\",\n \"name\": \"My new column\",\n \"color\": \"lean-lilac\",\n \"visible\": false,\n \"index\": 1\n },\n {\n \"id\": \"70cb04ee-b46e-4176-940c-cdf06cadef07\",\n \"name\": \"Actions\",\n \"color\": \"planning-pink\",\n \"visible\": false,\n \"index\": 2\n }\n]" + "body": "[\n {\n \"id\": \"526d5efb-281d-4f4f-b308-6537e6727381\",\n \"name\": \"Lean coffee\",\n \"description\": \"The column description\",\n \"color\": \"backlog-blue\",\n \"visible\": true,\n \"index\": 0\n },\n {\n \"id\": \"fecb346a-9d9e-43ca-b873-8fcb09f2b0c3\",\n \"name\": \"My new column\",\n \"description\": \"\",\n \"color\": \"lean-lilac\",\n \"visible\": false,\n \"index\": 1\n },\n {\n \"id\": \"70cb04ee-b46e-4176-940c-cdf06cadef07\",\n \"name\": \"Actions\",\n \"description\": \"\",\n \"color\": \"planning-pink\",\n \"visible\": false,\n \"index\": 2\n }\n]" } ] }, @@ -2782,20 +2787,22 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Successful GET request\", function () {", + "const res = pm.response.json();", + "", + "pm.test(\"Successful GET request\", () => {", " pm.expect(pm.response).to.have.status(200);", "});", "", - "pm.test(\"Check column\", function () {", - " var res = pm.response.json();", - "", + "pm.test(\"Check column\", () => {", " pm.expect(res.id).to.equal(pm.collectionVariables.get(\"column_id\"));", " pm.expect(res.name).to.equal(\"My new column\");", + " pm.expect(res.description).to.equal(\"The column description\");", " pm.expect(res.visible).to.eql(false);", " pm.expect(res.index).to.equal(1);", "})" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -2854,7 +2861,7 @@ } ], "cookie": [], - "body": "{\n \"id\": \"fecb346a-9d9e-43ca-b873-8fcb09f2b0c3\",\n \"name\": \"My new column\",\n \"color\": \"lean-lilac\",\n \"visible\": false,\n \"index\": 1\n}" + "body": "{\n \"id\": \"fecb346a-9d9e-43ca-b873-8fcb09f2b0c3\",\n \"name\": \"My new column\",\n \"description\": \"The column description\",\n \"color\": \"lean-lilac\",\n \"visible\": false,\n \"index\": 1\n}" } ] }, @@ -2865,20 +2872,22 @@ "listen": "test", "script": { "exec": [ + "const res = pm.response.json();", + "", "pm.test(\"Successful PUT request\", function () {", " pm.expect(pm.response).to.have.status(200);", "});", "", "pm.test(\"Check updated values\", function () {", - " var res = pm.response.json();", - "", " pm.expect(res.name).to.equal(\"Updated column name\");", + " pm.expect(res.description).to.equal(\"The updated column description\");", " pm.expect(res.color).to.equal(\"online-orange\");", " pm.expect(res.visible).to.equal(true);", " pm.expect(res.index).to.equal(0);", "})" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -2887,7 +2896,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"name\": \"Updated column name\",\r\n \"color\": \"online-orange\",\r\n \"visible\": true,\r\n \"index\": 0\r\n}", + "raw": "{\r\n \"name\": \"Updated column name\",\r\n \"description\": \"The updated column description\",\r\n \"color\": \"online-orange\",\r\n \"visible\": true,\r\n \"index\": 0\r\n}", "options": { "raw": { "language": "json" @@ -2955,7 +2964,7 @@ } ], "cookie": [], - "body": "{\n \"id\": \"fecb346a-9d9e-43ca-b873-8fcb09f2b0c3\",\n \"name\": \"Updated column name\",\n \"color\": \"online-orange\",\n \"visible\": true,\n \"index\": 0\n}" + "body": "{\n \"id\": \"fecb346a-9d9e-43ca-b873-8fcb09f2b0c3\",\n \"description\": \"The updated column description\",\n \"name\": \"Updated column name\",\n \"color\": \"online-orange\",\n \"visible\": true,\n \"index\": 0\n}" } ] }, @@ -3583,7 +3592,7 @@ " });", " ", " pm.test(\"PreRequestScript – safe note to delete\", function () {", - " res = response.json();", + " const res = response.json();", " pm.expect(res.id).to.exist;", " pm.collectionVariables.set(\"note_to_delete_id\", res.id); ", " })", From 90eee3e25c17f93119de5ac63cabd368abe6086a Mon Sep 17 00:00:00 2001 From: Benedict Homuth Date: Sun, 21 Jul 2024 11:40:36 +0200 Subject: [PATCH 4/7] updated go tests --- server/src/database/columns_test.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/server/src/database/columns_test.go b/server/src/database/columns_test.go index 72461b2bad..b487f50bc8 100644 --- a/server/src/database/columns_test.go +++ b/server/src/database/columns_test.go @@ -1,10 +1,11 @@ package database import ( + "testing" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "scrumlr.io/server/database/types" - "testing" ) var boardForColumnsTest uuid.UUID @@ -49,6 +50,8 @@ func TestRunnerForColumns(t *testing.T) { t.Run("Update=4", testMoveLastColumnOnFirstIndex) t.Run("Update=5", testMoveFirstColumnOnSecondIndex) t.Run("Update=6", testMoveSecondColumnOnFirstIndex) + + t.Run("Update=7", testUpdateDescription) } func testGetColumn(t *testing.T) { @@ -337,3 +340,17 @@ func verifyOrder(t *testing.T, ids ...uuid.UUID) { assert.Equal(t, index, value.Index) } } + +func testUpdateDescription(t *testing.T) { + column, err := testDb.UpdateColumn(ColumnUpdate{ + ID: firstColumn.ID, + Board: boardForColumnsTest, + Name: "FirstColumn", + Description: "Updated Column Description", + Color: types.ColorBacklogBlue, + Visible: true, + Index: 0, + }) + assert.Nil(t, err) + assert.Equal(t, "Updated Column Description", column.Description) +} From 9a591542d5fdb930e9599acf12be7fd5613be8e1 Mon Sep 17 00:00:00 2001 From: Benedict Homuth Date: Sun, 21 Jul 2024 11:51:01 +0200 Subject: [PATCH 5/7] whitespace change --- server/src/database/columns_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/database/columns_test.go b/server/src/database/columns_test.go index b487f50bc8..5abfd9ef05 100644 --- a/server/src/database/columns_test.go +++ b/server/src/database/columns_test.go @@ -50,7 +50,6 @@ func TestRunnerForColumns(t *testing.T) { t.Run("Update=4", testMoveLastColumnOnFirstIndex) t.Run("Update=5", testMoveFirstColumnOnSecondIndex) t.Run("Update=6", testMoveSecondColumnOnFirstIndex) - t.Run("Update=7", testUpdateDescription) } From 81e112adc126f959f01ae6f6a8a23c4397aa0a3b Mon Sep 17 00:00:00 2001 From: Benedict Homuth Date: Tue, 23 Jul 2024 09:00:09 +0200 Subject: [PATCH 6/7] renamed migration file --- ...lumn_subtitles.down.sql => 19_add_column_description.down.sql} | 0 ...d_column_subtitles.up.sql => 19_add_column_description.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename server/src/database/migrations/sql/{19_add_column_subtitles.down.sql => 19_add_column_description.down.sql} (100%) rename server/src/database/migrations/sql/{19_add_column_subtitles.up.sql => 19_add_column_description.up.sql} (100%) diff --git a/server/src/database/migrations/sql/19_add_column_subtitles.down.sql b/server/src/database/migrations/sql/19_add_column_description.down.sql similarity index 100% rename from server/src/database/migrations/sql/19_add_column_subtitles.down.sql rename to server/src/database/migrations/sql/19_add_column_description.down.sql diff --git a/server/src/database/migrations/sql/19_add_column_subtitles.up.sql b/server/src/database/migrations/sql/19_add_column_description.up.sql similarity index 100% rename from server/src/database/migrations/sql/19_add_column_subtitles.up.sql rename to server/src/database/migrations/sql/19_add_column_description.up.sql From 3bcb861008a947ff49c2fcc3263f1cb49a483d2c Mon Sep 17 00:00:00 2001 From: Benedict Homuth Date: Wed, 24 Jul 2024 11:10:17 +0200 Subject: [PATCH 7/7] added test for creating column with description --- server/src/database/columns_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/server/src/database/columns_test.go b/server/src/database/columns_test.go index 5abfd9ef05..00e23dc7ac 100644 --- a/server/src/database/columns_test.go +++ b/server/src/database/columns_test.go @@ -35,6 +35,7 @@ func TestRunnerForColumns(t *testing.T) { t.Run("Create=3", testCreateColumnWithExceptionallyHighIndex) t.Run("Create=4", testCreateColumnWithEmptyName) t.Run("Create=5", testCreateColumnWithEmptyColor) + t.Run("Create=6", testCreateColumnWithDescription) t.Run("Delete=0", testCreateColumnOnSecondIndex) t.Run("Delete=1", testDeleteColumnOnSecondIndex) @@ -181,6 +182,22 @@ func testCreateColumnWithEmptyColor(t *testing.T) { assert.NotNil(t, err) } +func testCreateColumnWithDescription(t *testing.T) { + aDescription := "A description" + column, err := testDb.CreateColumn(ColumnInsert{ + Board: boardForColumnsTest, + Name: "Column", + Color: types.ColorBacklogBlue, + Description: aDescription, + }) + assert.Nil(t, err) + assert.NotNil(t, column) + assert.Equal(t, aDescription, column.Description) + + // clean up to not crash other tests + _ = testDb.DeleteColumn(boardForColumnsTest, column.ID, uuid.New()) +} + func testDeleteColumnOnSecondIndex(t *testing.T) { err := testDb.DeleteColumn(boardForColumnsTest, columnInsertedFifth.ID, columnTestUser.ID) assert.Nil(t, err)