From dc7f73a573db70b4f25ff3b6952eeebc892d3ea2 Mon Sep 17 00:00:00 2001 From: Kevin Rieger Date: Thu, 18 Jan 2024 21:05:25 +0100 Subject: [PATCH] bug fixes and improvements --- .github/workflows/ci.yaml | 8 --- README.md | 70 +++++++++-------------- api/README.md | 4 +- api/Suggestions.md | 40 +++++++++++++ internal/routing/handlers/post_handler.go | 8 +-- internal/routing/handlers/user_handler.go | 4 +- 6 files changed, 77 insertions(+), 57 deletions(-) create mode 100644 api/Suggestions.md diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6c06e88..71778b1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,14 +16,6 @@ jobs: with: go-version-file: "./go.mod" - - name: Cache Go Modules - uses: actions/cache@v3 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Install Staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@latest diff --git a/README.md b/README.md index 0d18644..fa88927 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,32 @@ # Server-Alpha Server Repository for the Alpha Group (1) +# Status Badges + +CI and CD status badges for the main branch: + +[![Continuous Integration](https://github.com/wwi21seb-projekt/server-alpha/actions/workflows/ci.yaml/badge.svg)](https://github.com/wwi21seb-projekt/server-alpha/actions/workflows/ci.yaml) +[![Continuous Delivery](https://github.com/wwi21seb-projekt/server-alpha/actions/workflows/cd.yaml/badge.svg)](https://github.com/wwi21seb-projekt/server-alpha/actions/workflows/cd.yaml) + +DeepSource status badges for the main branch: + +[![DeepSource](https://app.deepsource.com/gh/wwi21seb-projekt/server-alpha.svg/?label=active+issues&show_trend=true&token=mcwBM2kkvsdD-hJRi0y1quuC)](https://app.deepsource.com/gh/wwi21seb-projekt/server-alpha/) +[![DeepSource](https://app.deepsource.com/gh/wwi21seb-projekt/server-alpha.svg/?label=resolved+issues&show_trend=true&token=mcwBM2kkvsdD-hJRi0y1quuC)](https://app.deepsource.com/gh/wwi21seb-projekt/server-alpha/) + +SonarCloud status badges for the main branch: + +[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=wwi21seb-projekt_server-alpha)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) + +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) +[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=bugs)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=wwi21seb-projekt_server-alpha&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=wwi21seb-projekt_server-alpha) + ## Prerequisites - [Docker](https://www.docker.com) @@ -84,46 +110,4 @@ The service can be configured via environment variables. The following variables | `JWT_PRIVATE_KEY` | The private key for JWT signing. | `secret` | | `JWT_PUBLIC_KEY` | The public key for JWT verification. | `secret` | | `MAILGUN_API_KEY` | The API key for Mailgun. | `secret` | -| `SERVER_IMAGE` | The image of the server. | `ghcr.io/wwi21seb-projekt/server-alpha:main` | - - -# Verbesserungsvorschläge/Änderungen - -## 1 User - -- [ ] **ALLE** API Ressourcen umbenennen (z.B. `users` -> `user`) - -### Subscriptions -- [ ] `GET /users/:user/subscribers` -> Alle Subscriber, die dem User mit der ID `:user` folgen - -- [ ] `GET /users/:user/subscriptions` -> Alle User, den der User mit der ID `:user` folgt -- [ ] `POST /users/:user/subscriptions` -> User mit der ID `:user` abonnieren -- [ ] `DELETE /users/:user/subscriptions` -> User mit der ID `:user` nicht mehr abonnieren - -## 2 Posts -- [ ] `POST /posts/` -> Post erstellen -- [ ] `GET /posts/:postid` -> Post mit der ID `:postid` -- [ ] `PUT /posts/:postid` -> Post mit der ID `:postid` aktualisieren -- [ ] `DELETE /posts/:postid` -> Post mit der ID `:postid` löschen - -### 2.1 Feed -- [ ] `GET /feed/private` -> Privater Feed des Users -- [ ] `GET /feed/public` -> Öffentlicher Feed von allen Posts - - -### 2.2 Comments -- [ ] `GET /posts/:postid/comments` -> Alle Kommentare zu Post mit der ID `:postid` -- [ ] `POST /posts/:postid/comments` -> Kommentar zu Post mit der ID `:postid` hinzufügen -- [ ] `GET /posts/:postid/comments/:commentid` -> Kommentar mit der ID `:commentid` abrufen -- [ ] `PUT /posts/:postid/comments/:commentid` -> Kommentar mit der ID `:commentid` aktualisieren -- [ ] `DELETE /posts/:postid/comments/:commentid` -> Kommentar mit der ID `:commentid` löschen - -### 2.3 Likes -- Posts - - [ ] `GET /posts/:postid/likes` -> Alle Likes zu Post mit der ID `:postid` (Gibt eine Liste mit Usern zurück) - - [ ] `POST /posts/:postid/likes` -> Like zu Post mit der ID `:postid` hinzufügen - - [ ] `DELETE /posts/:postid/likes` -> Like zu Post mit der ID `:postid` entfernen -- Comments - - [ ] `GET /posts/:postid/comments/:commentid/likes` -> Alle Likes zu Kommentar mit der ID `:commentid` (Gibt eine Liste mit Usern zurück) - - [ ] `POST /posts/:postid/comments/:commentid/likes` -> Like zu Kommentar mit der ID `:commentid` hinzufügen - - [ ] `DELETE /posts/:postid/comments/:commentid/likes` -> Like zu Kommentar mit der ID `:commentid` entfernen \ No newline at end of file +| `SERVER_IMAGE` | The image of the server. | `ghcr.io/wwi21seb-projekt/server-alpha:main` | \ No newline at end of file diff --git a/api/README.md b/api/README.md index 869738a..b338825 100644 --- a/api/README.md +++ b/api/README.md @@ -1,14 +1,16 @@ | Ticket | METHODE | Endpunkt | Beschreibung | Implementiert | |------------------------------------------|-----------------------------------------|--------------------------------------|----------------------------|---------------| +| ITERATION 1 | ITERATION 1 | ITERATION 1 | ITERATION 1 | ITERATION 1 | | [Trello Ticket 1] | POST | `/users` | Create User | ✅ | | [Trello Ticket 1] | POST | `/users/:username/activate` | Activate User | ✅ | | [Trello Ticket 1] | DELETE | `/users/:username/activate` | Resend token | ✅ | | [Trello Ticket 1] | POST | `/users/login` | Login | ✅ | | [Trello Ticket 2] | GET | `/imprint` | Impressum | ✅ | +| ITERATION 2 | ITERATION 2 | ITERATION 2 | ITERATION 2 | ITERATION 2 | | [Trello Ticket 3.1], [Trello Ticket 3.2] | POST | `/posts` | Create Posts | ✅ | | [Trello Ticket 5] | GET | `/users/:username` | Visit User Profile | ✅ | | [Trello Ticket 4] | GET | `/users?username&offset&limit` | Search User | ✅ | -| [Trello Ticket 4] | GET | `/users/:username/feed?offset&limit` | User Feed | ❌ | +| [Trello Ticket 4] | GET | `/users/:username/feed?offset&limit` | User Feed | ✅ | | [Trello Ticket 4] | POST | `/subscriptions` | Subscribe User | ✅ | | [Trello Ticket 4] | DELETE | `/subscriptions/:subscriptionId` | Unsubscribe User | ✅ | | [Trello Ticket 6.1] | GET | `/feed?postId&limit&feedType` | Get own or global feed | ✅ | diff --git a/api/Suggestions.md b/api/Suggestions.md new file mode 100644 index 0000000..d2037fa --- /dev/null +++ b/api/Suggestions.md @@ -0,0 +1,40 @@ +# Verbesserungsvorschläge/Änderungen + +## 1 User + +- [ ] **ALLE** API Ressourcen umbenennen (z.B. `users` -> `user`) + +### Subscriptions +- [ ] `GET /users/:user/subscribers` -> Alle Subscriber, die dem User mit der ID `:user` folgen + +- [ ] `GET /users/:user/subscriptions` -> Alle User, den der User mit der ID `:user` folgt +- [ ] `POST /users/:user/subscriptions` -> User mit der ID `:user` abonnieren +- [ ] `DELETE /users/:user/subscriptions` -> User mit der ID `:user` nicht mehr abonnieren + +## 2 Posts +- [ ] `POST /posts/` -> Post erstellen +- [ ] `GET /posts/:postid` -> Post mit der ID `:postid` +- [ ] `PUT /posts/:postid` -> Post mit der ID `:postid` aktualisieren +- [ ] `DELETE /posts/:postid` -> Post mit der ID `:postid` löschen + +### 2.1 Feed +- [ ] `GET /feed/private` -> Privater Feed des Users +- [ ] `GET /feed/public` -> Öffentlicher Feed von allen Posts + + +### 2.2 Comments +- [ ] `GET /posts/:postid/comments` -> Alle Kommentare zu Post mit der ID `:postid` +- [ ] `POST /posts/:postid/comments` -> Kommentar zu Post mit der ID `:postid` hinzufügen +- [ ] `GET /posts/:postid/comments/:commentid` -> Kommentar mit der ID `:commentid` abrufen +- [ ] `PUT /posts/:postid/comments/:commentid` -> Kommentar mit der ID `:commentid` aktualisieren +- [ ] `DELETE /posts/:postid/comments/:commentid` -> Kommentar mit der ID `:commentid` löschen + +### 2.3 Likes +- Posts + - [ ] `GET /posts/:postid/likes` -> Alle Likes zu Post mit der ID `:postid` (Gibt eine Liste mit Usern zurück) + - [ ] `POST /posts/:postid/likes` -> Like zu Post mit der ID `:postid` hinzufügen + - [ ] `DELETE /posts/:postid/likes` -> Like zu Post mit der ID `:postid` entfernen +- Comments + - [ ] `GET /posts/:postid/comments/:commentid/likes` -> Alle Likes zu Kommentar mit der ID `:commentid` (Gibt eine Liste mit Usern zurück) + - [ ] `POST /posts/:postid/comments/:commentid/likes` -> Like zu Kommentar mit der ID `:commentid` hinzufügen + - [ ] `DELETE /posts/:postid/comments/:commentid/likes` -> Like zu Kommentar mit der ID `:commentid` entfernen \ No newline at end of file diff --git a/internal/routing/handlers/post_handler.go b/internal/routing/handlers/post_handler.go index 5fe01e6..d0d3acb 100644 --- a/internal/routing/handlers/post_handler.go +++ b/internal/routing/handlers/post_handler.go @@ -212,13 +212,13 @@ func retrieveFeed(ctx context.Context, tx pgx.Tx, w http.ResponseWriter, r *http if !publicFeedWanted { userId = claims.(jwt.MapClaims)["sub"].(string) countQuery = `SELECT COUNT(*) FROM alpha_schema.posts - INNER JOIN alpha_schema.users ON author_id = user_id - INNER JOIN alpha_schema.subscriptions ON user_id = subscriptions.subscriber_id + INNER JOIN alpha_schema.subscriptions ON posts.author_id = subscriptions.subscribee_id + INNER JOIN alpha_schema.users ON posts.author_id = users.user_id WHERE subscriptions.subscriber_id` + fmt.Sprintf(" = $%d", currentCountQueryIndex) currentCountQueryIndex++ dataQuery = `SELECT post_id, username, nickname, profile_picture_url, content, posts.created_at FROM alpha_schema.posts - INNER JOIN alpha_schema.users ON author_id = user_id - INNER JOIN alpha_schema.subscriptions ON user_id = subscriptions.subscriber_id + INNER JOIN alpha_schema.subscriptions ON posts.author_id = subscriptions.subscribee_id + INNER JOIN alpha_schema.users ON posts.author_id = users.user_id WHERE subscriptions.subscriber_id` + fmt.Sprintf(" = $%d", currentDataQueryIndex) currentDataQueryIndex++ diff --git a/internal/routing/handlers/user_handler.go b/internal/routing/handlers/user_handler.go index d890de9..d9ff445 100644 --- a/internal/routing/handlers/user_handler.go +++ b/internal/routing/handlers/user_handler.go @@ -504,6 +504,7 @@ func (handler *UserHandler) Subscribe(w http.ResponseWriter, r *http.Request) { utils.WriteAndLogError(w, schemas.UserNotFound, http.StatusNotFound, err) return } + utils.WriteAndLogError(w, schemas.DatabaseError, http.StatusInternalServerError, err) return } @@ -523,6 +524,7 @@ func (handler *UserHandler) Subscribe(w http.ResponseWriter, r *http.Request) { queryString = "SELECT subscription_id FROM alpha_schema.subscriptions WHERE subscriber_id = $1 AND subscribee_id = $2" rows := tx.QueryRow(transactionCtx, queryString, jwtUserId, subscribeeId) var subscriptionId uuid.UUID + if err := rows.Scan(&subscriptionId); err != nil { if !errors.Is(err, pgx.ErrNoRows) { utils.WriteAndLogError(w, schemas.DatabaseError, http.StatusInternalServerError, err) @@ -531,6 +533,7 @@ func (handler *UserHandler) Subscribe(w http.ResponseWriter, r *http.Request) { } if subscriptionId != uuid.Nil { + // User is already subscribed, since the subscriptionId is not nil utils.WriteAndLogError(w, schemas.SubscriptionAlreadyExists, http.StatusConflict, errors.New("subscription already exists")) return } @@ -965,7 +968,6 @@ func parsePaginationParams(r *http.Request) (int, int, error) { } func sendPaginatedResponse(w http.ResponseWriter, records interface{}, offset, limit, totalRecords int) { - if offset > totalRecords { utils.WriteAndLogError(w, schemas.BadRequest, http.StatusBadRequest, errors.New("offset invalid")) return