From 4ce2131ac453673e5393708eeaf0ab47ea0bc646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Wysoki=C5=84ski?= Date: Tue, 20 Feb 2024 07:50:16 +0100 Subject: [PATCH] feat: api - GET /api/v2/versions/{versionCode}/servers/{serverKey}/tribes - new query param 'tag' --- api/openapi3.yml | 9 +++ internal/adapter/repository_bun_tribe.go | 4 ++ internal/adapter/repository_tribe_test.go | 35 +++++++++++ internal/domain/tribe.go | 24 +++++++ internal/domain/tribe_test.go | 77 +++++++++++++++++++++++ internal/port/handler_http_api_tribe.go | 7 +++ 6 files changed, 156 insertions(+) diff --git a/api/openapi3.yml b/api/openapi3.yml index eb04ef8..cbcd370 100644 --- a/api/openapi3.yml +++ b/api/openapi3.yml @@ -143,6 +143,7 @@ paths: - $ref: "#/components/parameters/LimitQueryParam" - $ref: "#/components/parameters/TribeDeletedQueryParam" - $ref: "#/components/parameters/TribeSortQueryParam" + - $ref: "#/components/parameters/TribeTagQueryParam" responses: 200: $ref: "#/components/responses/ListTribesResponse" @@ -972,6 +973,14 @@ components: - deletedAt:ASC - deletedAt:DESC maxItems: 2 + TribeTagQueryParam: + name: tag + in: query + schema: + type: array + items: + type: string + maxItems: 100 VersionCodePathParam: in: path name: versionCode diff --git a/internal/adapter/repository_bun_tribe.go b/internal/adapter/repository_bun_tribe.go index a40a44c..2a383a2 100644 --- a/internal/adapter/repository_bun_tribe.go +++ b/internal/adapter/repository_bun_tribe.go @@ -165,6 +165,10 @@ func (a listTribesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery { q = q.Where("tribe.server_key IN (?)", bun.In(serverKeys)) } + if tags := a.params.Tags(); len(tags) > 0 { + q = q.Where("tribe.tag IN (?)", bun.In(tags)) + } + if deleted := a.params.Deleted(); deleted.Valid { if deleted.Value { q = q.Where("tribe.deleted_at IS NOT NULL") diff --git a/internal/adapter/repository_tribe_test.go b/internal/adapter/repository_tribe_test.go index e846385..a49c132 100644 --- a/internal/adapter/repository_tribe_test.go +++ b/internal/adapter/repository_tribe_test.go @@ -460,6 +460,41 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) require.NoError(t, err) }, }, + { + name: "OK: tags serverKeys", + params: func(t *testing.T) domain.ListTribesParams { + t.Helper() + + params := domain.NewListTribesParams() + + res, err := repos.tribe.List(ctx, params) + require.NoError(t, err) + require.NotEmpty(t, len(res.Tribes())) + randTribe := res.Tribes()[0] + + require.NoError(t, params.SetTags([]string{randTribe.Tag()})) + require.NoError(t, params.SetServerKeys([]string{randTribe.ServerKey()})) + + return params + }, + assertResult: func(t *testing.T, params domain.ListTribesParams, res domain.ListTribesResult) { + t.Helper() + + tags := params.Tags() + serverKeys := params.ServerKeys() + + tribes := res.Tribes() + assert.NotEmpty(t, tribes) + for _, tr := range tribes { + assert.True(t, slices.Contains(tags, tr.Tag())) + assert.True(t, slices.Contains(serverKeys, tr.ServerKey())) + } + }, + assertError: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err) + }, + }, { name: "OK: deleted=true", params: func(t *testing.T) domain.ListTribesParams { diff --git a/internal/domain/tribe.go b/internal/domain/tribe.go index d8ef8e8..215d37c 100644 --- a/internal/domain/tribe.go +++ b/internal/domain/tribe.go @@ -618,6 +618,7 @@ func (tc TribeCursor) Encode() string { type ListTribesParams struct { ids []int serverKeys []string + tags []string deleted NullBool sort []TribeSort cursor TribeCursor @@ -669,6 +670,29 @@ func (params *ListTribesParams) SetServerKeys(serverKeys []string) error { return nil } +func (params *ListTribesParams) Tags() []string { + return params.tags +} + +const ( + tribeTagsMinLength = 1 + tribeTagsMaxLength = 100 +) + +func (params *ListTribesParams) SetTags(tags []string) error { + if err := validateSliceLen(tags, tribeTagsMinLength, tribeTagsMaxLength); err != nil { + return ValidationError{ + Model: listTribesParamsModelName, + Field: "tags", + Err: err, + } + } + + params.tags = tags + + return nil +} + func (params *ListTribesParams) Deleted() NullBool { return params.deleted } diff --git a/internal/domain/tribe_test.go b/internal/domain/tribe_test.go index afc6079..ee449d7 100644 --- a/internal/domain/tribe_test.go +++ b/internal/domain/tribe_test.go @@ -430,6 +430,83 @@ func TestListTribesParams_SetIDs(t *testing.T) { } } +func TestListTribesParams_SetTags(t *testing.T) { + t.Parallel() + + type args struct { + tags []string + } + + tests := []struct { + name string + args args + expectedErr error + }{ + { + name: "OK", + args: args{ + tags: []string{ + domaintest.RandTribeTag(), + domaintest.RandTribeTag(), + domaintest.RandTribeTag(), + domaintest.RandTribeTag(), + domaintest.RandTribeTag(), + }, + }, + }, + { + name: "ERR: len(tags) < 1", + args: args{ + tags: nil, + }, + expectedErr: domain.ValidationError{ + Model: "ListTribesParams", + Field: "tags", + Err: domain.LenOutOfRangeError{ + Min: 1, + Max: 100, + Current: 0, + }, + }, + }, + { + name: "ERR: len(tags) > 100", + args: args{ + tags: func() []string { + tags := make([]string, 101) + for i := range tags { + tags[i] = domaintest.RandTribeTag() + } + return tags + }(), + }, + expectedErr: domain.ValidationError{ + Model: "ListTribesParams", + Field: "tags", + Err: domain.LenOutOfRangeError{ + Min: 1, + Max: 100, + Current: 101, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + params := domain.NewListTribesParams() + + require.ErrorIs(t, params.SetTags(tt.args.tags), tt.expectedErr) + if tt.expectedErr != nil { + return + } + assert.Equal(t, tt.args.tags, params.Tags()) + }) + } +} + func TestListTribesParams_SetSort(t *testing.T) { t.Parallel() diff --git a/internal/port/handler_http_api_tribe.go b/internal/port/handler_http_api_tribe.go index f8f1e6b..9df328d 100644 --- a/internal/port/handler_http_api_tribe.go +++ b/internal/port/handler_http_api_tribe.go @@ -35,6 +35,13 @@ func (h *apiHTTPHandler) ListTribes( return } + if params.Tag != nil { + if err := domainParams.SetTags(*params.Tag); err != nil { + apiErrorRenderer{errors: []error{err}, formatErrorPath: formatListTribesParamsErrorPath}.render(w, r) + return + } + } + if params.Deleted != nil { if err := domainParams.SetDeleted(domain.NullBool{ Value: *params.Deleted,