From ca13064aa18498f0036774c1ff030d455fa492f8 Mon Sep 17 00:00:00 2001 From: "popov.dmitriy" <dp@sessia.dev> Date: Thu, 15 May 2025 18:19:26 +0300 Subject: [PATCH 1/3] Fix filter --- request/pagination.go | 21 ++++++++++ request/pagination_test.go | 80 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 request/pagination.go create mode 100644 request/pagination_test.go diff --git a/request/pagination.go b/request/pagination.go new file mode 100644 index 0000000..fccf52d --- /dev/null +++ b/request/pagination.go @@ -0,0 +1,21 @@ +package request + +import "github.com/go-playground/validator/v10" + +type Pagination struct { + Page int `query:"page" validate:"min=1"` + PageSize int `query:"page_size" validate:"min=1,max=100"` +} + +func (p *Pagination) Validate() error { + if p.Page == 0 { + p.Page = 1 + } + + if p.PageSize == 0 { + p.PageSize = 10 + } + + validate := validator.New() + return validate.Struct(p) +} diff --git a/request/pagination_test.go b/request/pagination_test.go new file mode 100644 index 0000000..68e1fee --- /dev/null +++ b/request/pagination_test.go @@ -0,0 +1,80 @@ +package request + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPagination_Validate(t *testing.T) { + tests := []struct { + name string + input Pagination + expectedPage int + expectedSize int + expectError bool + errorContains string + }{ + { + name: "default values set when zero", + input: Pagination{Page: 0, PageSize: 0}, + expectedPage: 1, + expectedSize: 10, + expectError: false, + }, + { + name: "valid input", + input: Pagination{Page: 2, PageSize: 50}, + expectedPage: 2, + expectedSize: 50, + expectError: false, + }, + { + name: "page less than minimum", + input: Pagination{Page: 0, PageSize: 10}, + expectedPage: 1, // default set + expectedSize: 10, + expectError: false, + }, + { + name: "page size less than minimum", + input: Pagination{Page: 1, PageSize: 0}, + expectedPage: 1, + expectedSize: 10, // default set + expectError: false, + }, + { + name: "page size too large", + input: Pagination{Page: 1, PageSize: 101}, + expectedPage: 1, + expectedSize: 101, + expectError: true, + errorContains: "PageSize", + }, + { + name: "page less than 1 after default", + input: Pagination{Page: -1, PageSize: 10}, + expectedPage: -1, + expectedSize: 10, + expectError: true, + errorContains: "Page", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.input.Validate() + assert.Equal(t, tt.expectedPage, tt.input.Page) + assert.Equal(t, tt.expectedSize, tt.input.PageSize) + + if tt.expectError { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + } + }) + } +} -- GitLab From 00f1ebba08ed02dbecc321499f783d03fe236910 Mon Sep 17 00:00:00 2001 From: "popov.dmitriy" <dp@sessia.dev> Date: Thu, 15 May 2025 18:19:41 +0300 Subject: [PATCH 2/3] Add pagination --- go.mod | 8 +++++ go.sum | 18 ++++++++++ request/filter.go | 79 ++++++---------------------------------- request/filter_test.go | 82 ++++++++++++++++++------------------------ 4 files changed, 71 insertions(+), 116 deletions(-) diff --git a/go.mod b/go.mod index c3af6e8..a5fdb90 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module gitlab.sessia.com/sdk/api-response-request go 1.23.3 require ( + github.com/go-playground/validator/v10 v10.26.0 github.com/gofiber/fiber/v2 v2.52.6 github.com/stretchr/testify v1.10.0 ) @@ -10,8 +11,12 @@ require ( require ( github.com/andybalholm/brotli v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.18.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect @@ -19,6 +24,9 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.59.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/net v0.35.0 // indirect golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index adee732..f66153b 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,24 @@ github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7X github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -27,9 +39,15 @@ github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDp github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/request/filter.go b/request/filter.go index 2fbdcc4..bed0367 100644 --- a/request/filter.go +++ b/request/filter.go @@ -1,95 +1,38 @@ package request import ( - "fmt" "net/url" "strings" - "time" ) -type FilterOperation interface { - Field() string - Operation() string -} - -type SimpleFilter struct { - FieldName string - Op string +type FilterOperation struct { + Field string + Operation string Value string } -func (f SimpleFilter) Field() string { return f.FieldName } -func (f SimpleFilter) Operation() string { return f.Op } - -type RangeFilter struct { - FieldName string - Range DateRange -} - -func (r RangeFilter) Field() string { return r.FieldName } -func (r RangeFilter) Operation() string { return "range" } - -type DateRange struct { - Begin *time.Time - End *time.Time -} - type FilterSet struct { Operations []FilterOperation } func (fs *FilterSet) ParseFromQueries(values url.Values) error { - const layout = time.RFC3339 - - rangeMap := make(map[string]*RangeFilter) - for key, valArr := range values { if !strings.HasPrefix(key, "filter[") || !strings.HasSuffix(key, "]") || len(valArr) == 0 { continue } trimmed := strings.TrimSuffix(strings.TrimPrefix(key, "filter["), "]") - parts := strings.Split(trimmed, "][") - - switch len(parts) { - case 2: - fs.Operations = append(fs.Operations, SimpleFilter{ - FieldName: parts[0], - Op: parts[1], - Value: valArr[0], - }) - case 3: - field := parts[0] - op := parts[1] - bound := parts[2] - - if op != "range" { - continue - } - - rf, exists := rangeMap[field] - if !exists { - rf = &RangeFilter{FieldName: field} - rangeMap[field] = rf - } - - parsedTime, err := time.Parse(layout, valArr[0]) - if err != nil { - return fmt.Errorf("invalid time format for field %s %s: %w", field, bound, err) - } - switch bound { - case "begin": - rf.Range.Begin = &parsedTime - case "end": - rf.Range.End = &parsedTime - } + parts := strings.Split(trimmed, "][") + if len(parts) != 2 { + continue } - } - for _, rf := range rangeMap { - fs.Operations = append(fs.Operations, rf) + fs.Operations = append(fs.Operations, FilterOperation{ + Field: parts[0], + Operation: parts[1], + Value: valArr[0], + }) } - return nil } diff --git a/request/filter_test.go b/request/filter_test.go index 91b971b..fca378f 100644 --- a/request/filter_test.go +++ b/request/filter_test.go @@ -3,65 +3,51 @@ package request import ( "net/url" "testing" - "time" "github.com/stretchr/testify/assert" ) func TestFilterSet_ParseFromQueries(t *testing.T) { values := url.Values{} - values.Set("filter[createdAt][gte]", "2024-01-01T00:00:00Z") + values.Set("filter[createdAt][gte]", "2024-01-01") + values.Set("filter[createdAt][lte]", "2024-02-01") + values.Set("filter[approve][eq]", "true") values.Set("filter[amount][lte]", "1000") values.Set("filter[status][eq]", "approved") - values.Set("filter[decisionEvaluatedAt][range][begin]", "2025-05-01T00:00:00Z") - values.Set("filter[decisionEvaluatedAt][range][end]", "2025-05-02T00:00:00Z") var fs FilterSet err := fs.ParseFromQueries(values) assert.NoError(t, err) - assert.Len(t, fs.Operations, 4) - - var foundCreatedAt, foundAmount, foundStatus, foundRange bool - - for _, op := range fs.Operations { - switch f := op.(type) { - case SimpleFilter: - switch f.FieldName { - case "createdAt": - assert.Equal(t, "gte", f.Op) - assert.Equal(t, "2024-01-01T00:00:00Z", f.Value) - foundCreatedAt = true - case "amount": - assert.Equal(t, "lte", f.Op) - assert.Equal(t, "1000", f.Value) - foundAmount = true - case "status": - assert.Equal(t, "eq", f.Op) - assert.Equal(t, "approved", f.Value) - foundStatus = true - default: - t.Errorf("Unexpected SimpleFilter field: %s", f.FieldName) - } - case *RangeFilter: - assert.Equal(t, "decisionEvaluatedAt", f.FieldName) - assert.NotNil(t, f.Range.Begin) - assert.NotNil(t, f.Range.End) - - beginExpected, _ := time.Parse(time.RFC3339, "2025-05-01T00:00:00Z") - endExpected, _ := time.Parse(time.RFC3339, "2025-05-02T00:00:00Z") - - assert.True(t, f.Range.Begin.Equal(beginExpected)) - assert.True(t, f.Range.End.Equal(endExpected)) - - foundRange = true - default: - t.Errorf("Unknown filter operation type %T", op) - } - } - - assert.True(t, foundCreatedAt, "createdAt filter not found") - assert.True(t, foundAmount, "amount filter not found") - assert.True(t, foundStatus, "status filter not found") - assert.True(t, foundRange, "range filter not found") + assert.Len(t, fs.Operations, 5) + + assert.Contains(t, fs.Operations, FilterOperation{ + Field: "createdAt", + Operation: "gte", + Value: "2024-01-01", + }) + + assert.Contains(t, fs.Operations, FilterOperation{ + Field: "createdAt", + Operation: "lte", + Value: "2024-02-01", + }) + + assert.Contains(t, fs.Operations, FilterOperation{ + Field: "approve", + Operation: "eq", + Value: "1000", + }) + + assert.Contains(t, fs.Operations, FilterOperation{ + Field: "amount", + Operation: "lte", + Value: "1000", + }) + + assert.Contains(t, fs.Operations, FilterOperation{ + Field: "status", + Operation: "eq", + Value: "approved", + }) } -- GitLab From af3a6384a84c8c2df2d97a9f30f95058fbfea5a6 Mon Sep 17 00:00:00 2001 From: "popov.dmitriy" <dp@sessia.dev> Date: Thu, 15 May 2025 18:32:17 +0300 Subject: [PATCH 3/3] Refactoring --- request/filter_test.go | 51 +++++++++++-- request/group_test.go | 19 ++++- request/pagination_test.go | 131 +++++++++++++++----------------- response/{model.go => error.go} | 35 +-------- response/pagination.go | 32 ++++++++ response/simple.go | 9 +++ 6 files changed, 160 insertions(+), 117 deletions(-) rename response/{model.go => error.go} (64%) create mode 100644 response/pagination.go create mode 100644 response/simple.go diff --git a/request/filter_test.go b/request/filter_test.go index fca378f..ffd0fca 100644 --- a/request/filter_test.go +++ b/request/filter_test.go @@ -7,44 +7,79 @@ import ( "github.com/stretchr/testify/assert" ) -func TestFilterSet_ParseFromQueries(t *testing.T) { +func TestFilterSet_ParseFromQueries_CreatedAtGte(t *testing.T) { values := url.Values{} values.Set("filter[createdAt][gte]", "2024-01-01") - values.Set("filter[createdAt][lte]", "2024-02-01") - values.Set("filter[approve][eq]", "true") - values.Set("filter[amount][lte]", "1000") - values.Set("filter[status][eq]", "approved") var fs FilterSet err := fs.ParseFromQueries(values) assert.NoError(t, err) - assert.Len(t, fs.Operations, 5) - + assert.Len(t, fs.Operations, 1) assert.Contains(t, fs.Operations, FilterOperation{ Field: "createdAt", Operation: "gte", Value: "2024-01-01", }) +} +func TestFilterSet_ParseFromQueries_CreatedAtLte(t *testing.T) { + values := url.Values{} + values.Set("filter[createdAt][lte]", "2024-02-01") + + var fs FilterSet + err := fs.ParseFromQueries(values) + + assert.NoError(t, err) + assert.Len(t, fs.Operations, 1) assert.Contains(t, fs.Operations, FilterOperation{ Field: "createdAt", Operation: "lte", Value: "2024-02-01", }) +} + +func TestFilterSet_ParseFromQueries_ApproveEq(t *testing.T) { + values := url.Values{} + values.Set("filter[approve][eq]", "true") + + var fs FilterSet + err := fs.ParseFromQueries(values) + assert.NoError(t, err) + assert.Len(t, fs.Operations, 1) assert.Contains(t, fs.Operations, FilterOperation{ Field: "approve", Operation: "eq", - Value: "1000", + Value: "true", }) +} + +func TestFilterSet_ParseFromQueries_AmountLte(t *testing.T) { + values := url.Values{} + values.Set("filter[amount][lte]", "1000") + + var fs FilterSet + err := fs.ParseFromQueries(values) + assert.NoError(t, err) + assert.Len(t, fs.Operations, 1) assert.Contains(t, fs.Operations, FilterOperation{ Field: "amount", Operation: "lte", Value: "1000", }) +} + +func TestFilterSet_ParseFromQueries_StatusEq(t *testing.T) { + values := url.Values{} + values.Set("filter[status][eq]", "approved") + var fs FilterSet + err := fs.ParseFromQueries(values) + + assert.NoError(t, err) + assert.Len(t, fs.Operations, 1) assert.Contains(t, fs.Operations, FilterOperation{ Field: "status", Operation: "eq", diff --git a/request/group_test.go b/request/group_test.go index b748d36..705edb3 100644 --- a/request/group_test.go +++ b/request/group_test.go @@ -1,26 +1,37 @@ package request import ( - "github.com/stretchr/testify/assert" "net/url" "testing" + + "github.com/stretchr/testify/assert" ) -func TestGroupingSet_ParseFromQueries(t *testing.T) { +func TestGroupingSet_ParseFromQueries_CreatedAtMonth(t *testing.T) { values := url.Values{} values.Set("group[createdAt]", "month") - values.Set("group[status]", "raw") var gs GroupingSet err := gs.ParseFromQueries(values) assert.NoError(t, err) - assert.Len(t, gs.Groups, 2) + assert.Len(t, gs.Groups, 1) assert.Contains(t, gs.Groups, GroupingMethod{ Field: "createdAt", Method: "month", }) +} + +func TestGroupingSet_ParseFromQueries_StatusRaw(t *testing.T) { + values := url.Values{} + values.Set("group[status]", "raw") + + var gs GroupingSet + err := gs.ParseFromQueries(values) + + assert.NoError(t, err) + assert.Len(t, gs.Groups, 1) assert.Contains(t, gs.Groups, GroupingMethod{ Field: "status", diff --git a/request/pagination_test.go b/request/pagination_test.go index 68e1fee..b6f4673 100644 --- a/request/pagination_test.go +++ b/request/pagination_test.go @@ -6,75 +6,64 @@ import ( "github.com/stretchr/testify/assert" ) -func TestPagination_Validate(t *testing.T) { - tests := []struct { - name string - input Pagination - expectedPage int - expectedSize int - expectError bool - errorContains string - }{ - { - name: "default values set when zero", - input: Pagination{Page: 0, PageSize: 0}, - expectedPage: 1, - expectedSize: 10, - expectError: false, - }, - { - name: "valid input", - input: Pagination{Page: 2, PageSize: 50}, - expectedPage: 2, - expectedSize: 50, - expectError: false, - }, - { - name: "page less than minimum", - input: Pagination{Page: 0, PageSize: 10}, - expectedPage: 1, // default set - expectedSize: 10, - expectError: false, - }, - { - name: "page size less than minimum", - input: Pagination{Page: 1, PageSize: 0}, - expectedPage: 1, - expectedSize: 10, // default set - expectError: false, - }, - { - name: "page size too large", - input: Pagination{Page: 1, PageSize: 101}, - expectedPage: 1, - expectedSize: 101, - expectError: true, - errorContains: "PageSize", - }, - { - name: "page less than 1 after default", - input: Pagination{Page: -1, PageSize: 10}, - expectedPage: -1, - expectedSize: 10, - expectError: true, - errorContains: "Page", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.input.Validate() - assert.Equal(t, tt.expectedPage, tt.input.Page) - assert.Equal(t, tt.expectedSize, tt.input.PageSize) - - if tt.expectError { - assert.Error(t, err) - if err != nil { - assert.Contains(t, err.Error(), tt.errorContains) - } - } else { - assert.NoError(t, err) - } - }) - } +func TestPagination_Validate_DefaultValues(t *testing.T) { + p := Pagination{Page: 0, PageSize: 0} + + err := p.Validate() + + assert.NoError(t, err) + assert.Equal(t, 1, p.Page) + assert.Equal(t, 10, p.PageSize) +} + +func TestPagination_Validate_ValidInput(t *testing.T) { + p := Pagination{Page: 2, PageSize: 50} + + err := p.Validate() + + assert.NoError(t, err) + assert.Equal(t, 2, p.Page) + assert.Equal(t, 50, p.PageSize) +} + +func TestPagination_Validate_PageLessThanMinimum(t *testing.T) { + p := Pagination{Page: 0, PageSize: 10} + + err := p.Validate() + + assert.NoError(t, err) + assert.Equal(t, 1, p.Page) + assert.Equal(t, 10, p.PageSize) +} + +func TestPagination_Validate_PageSizeLessThanMinimum(t *testing.T) { + p := Pagination{Page: 1, PageSize: 0} + + err := p.Validate() + + assert.NoError(t, err) + assert.Equal(t, 1, p.Page) + assert.Equal(t, 10, p.PageSize) +} + +func TestPagination_Validate_PageSizeTooLarge(t *testing.T) { + p := Pagination{Page: 1, PageSize: 101} + + err := p.Validate() + + assert.Error(t, err) + assert.Contains(t, err.Error(), "PageSize") + assert.Equal(t, 1, p.Page) + assert.Equal(t, 101, p.PageSize) +} + +func TestPagination_Validate_PageNegative(t *testing.T) { + p := Pagination{Page: -1, PageSize: 10} + + err := p.Validate() + + assert.Error(t, err) + assert.Contains(t, err.Error(), "Page") + assert.Equal(t, -1, p.Page) + assert.Equal(t, 10, p.PageSize) } diff --git a/response/model.go b/response/error.go similarity index 64% rename from response/model.go rename to response/error.go index b0ff9af..5171d2b 100644 --- a/response/model.go +++ b/response/error.go @@ -1,39 +1,6 @@ package response -import ( - "github.com/gofiber/fiber/v2" - "math" -) - -func NewResponse(c *fiber.Ctx, data any) error { - return c.JSON(data) -} - -type PaginationResponse struct { - Data interface{} `json:"data"` - Pagination Pagination `json:"pagination"` -} - -type Pagination struct { - TotalPages int `json:"totalPages"` - CurrentPage int `json:"currentPage"` - TotalItems int64 `json:"totalItems"` - PageSize int `json:"pageSize"` -} - -func NewPaginationResponse(c *fiber.Ctx, data any, page int, pageSize int, total int64) error { - totalPages := int(math.Ceil(float64(total) / float64(pageSize))) - - return c.JSON(PaginationResponse{ - Data: data, - Pagination: Pagination{ - TotalPages: totalPages, - CurrentPage: page, - TotalItems: total, - PageSize: pageSize, - }, - }) -} +import "github.com/gofiber/fiber/v2" type ErrorResponse struct { Code CodeErrorResponse `json:"code"` diff --git a/response/pagination.go b/response/pagination.go new file mode 100644 index 0000000..6534e93 --- /dev/null +++ b/response/pagination.go @@ -0,0 +1,32 @@ +package response + +import ( + "github.com/gofiber/fiber/v2" + "math" +) + +type PaginationResponse struct { + Data interface{} `json:"data"` + Pagination Pagination `json:"pagination"` +} + +type Pagination struct { + TotalPages int `json:"totalPages"` + CurrentPage int `json:"currentPage"` + TotalItems int64 `json:"totalItems"` + PageSize int `json:"pageSize"` +} + +func NewPaginationResponse(c *fiber.Ctx, data any, page int, pageSize int, total int64) error { + totalPages := int(math.Ceil(float64(total) / float64(pageSize))) + + return c.JSON(PaginationResponse{ + Data: data, + Pagination: Pagination{ + TotalPages: totalPages, + CurrentPage: page, + TotalItems: total, + PageSize: pageSize, + }, + }) +} diff --git a/response/simple.go b/response/simple.go new file mode 100644 index 0000000..7085c12 --- /dev/null +++ b/response/simple.go @@ -0,0 +1,9 @@ +package response + +import ( + "github.com/gofiber/fiber/v2" +) + +func NewResponse(c *fiber.Ctx, data any) error { + return c.JSON(data) +} -- GitLab