feat: api - add a new endpoint - GET /api/v2/versions/{versionCode}
This commit is contained in:
parent
28877f1b9b
commit
a66bbb0b2f
|
@ -36,6 +36,19 @@ paths:
|
||||||
$ref: "#/components/responses/ListVersionsResponse"
|
$ref: "#/components/responses/ListVersionsResponse"
|
||||||
default:
|
default:
|
||||||
$ref: "#/components/responses/ErrorResponse"
|
$ref: "#/components/responses/ErrorResponse"
|
||||||
|
/v2/versions/{versionCode}:
|
||||||
|
get:
|
||||||
|
operationId: getVersion
|
||||||
|
tags:
|
||||||
|
- versions
|
||||||
|
description: Get a version
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/components/parameters/VersionCodePathParam"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
$ref: "#/components/responses/GetVersionResponse"
|
||||||
|
default:
|
||||||
|
$ref: "#/components/responses/ErrorResponse"
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
Error:
|
Error:
|
||||||
|
@ -81,29 +94,23 @@ components:
|
||||||
timezone:
|
timezone:
|
||||||
type: string
|
type: string
|
||||||
example: Europe/Warsaw
|
example: Europe/Warsaw
|
||||||
PaginationResponse:
|
Cursor:
|
||||||
type: object
|
type: object
|
||||||
|
x-go-type-skip-optional-pointer: true
|
||||||
properties:
|
properties:
|
||||||
self:
|
self:
|
||||||
description: Cursor pointing to the current page.
|
description: Cursor pointing to the current page.
|
||||||
type: string
|
type: string
|
||||||
x-go-type-skip-optional-pointer: true
|
x-go-type-skip-optional-pointer: true
|
||||||
first:
|
|
||||||
description: Cursor pointing to the first page.
|
|
||||||
type: string
|
|
||||||
x-go-type-skip-optional-pointer: true
|
|
||||||
prev:
|
|
||||||
description: Cursor pointing to the previous page.
|
|
||||||
type: string
|
|
||||||
x-go-type-skip-optional-pointer: true
|
|
||||||
next:
|
next:
|
||||||
description: Cursor pointing to the next page.
|
description: Cursor pointing to the next page.
|
||||||
type: string
|
type: string
|
||||||
x-go-type-skip-optional-pointer: true
|
x-go-type-skip-optional-pointer: true
|
||||||
last:
|
PaginationResponse:
|
||||||
description: Cursor pointing to the last page.
|
type: object
|
||||||
type: string
|
properties:
|
||||||
x-go-type-skip-optional-pointer: true
|
cursor:
|
||||||
|
$ref: "#/components/schemas/Cursor"
|
||||||
parameters:
|
parameters:
|
||||||
CursorQueryParam:
|
CursorQueryParam:
|
||||||
in: query
|
in: query
|
||||||
|
@ -117,6 +124,12 @@ components:
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
required: false
|
required: false
|
||||||
|
VersionCodePathParam:
|
||||||
|
in: path
|
||||||
|
name: versionCode
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
responses:
|
responses:
|
||||||
ListVersionsResponse:
|
ListVersionsResponse:
|
||||||
description: ""
|
description: ""
|
||||||
|
@ -127,12 +140,23 @@ components:
|
||||||
- $ref: "#/components/schemas/PaginationResponse"
|
- $ref: "#/components/schemas/PaginationResponse"
|
||||||
- type: object
|
- type: object
|
||||||
required:
|
required:
|
||||||
- items
|
- data
|
||||||
properties:
|
properties:
|
||||||
items:
|
data:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Version"
|
$ref: "#/components/schemas/Version"
|
||||||
|
GetVersionResponse:
|
||||||
|
description: ""
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- data
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: "#/components/schemas/Version"
|
||||||
ErrorResponse:
|
ErrorResponse:
|
||||||
description: Default error response.
|
description: Default error response.
|
||||||
content:
|
content:
|
||||||
|
|
|
@ -45,6 +45,10 @@ type listVersionsParamsApplier struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a listVersionsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
func (a listVersionsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
if codes := a.params.Codes(); len(codes) > 0 {
|
||||||
|
q = q.Where("version.code IN (?)", bun.In(codes))
|
||||||
|
}
|
||||||
|
|
||||||
for _, s := range a.params.Sort() {
|
for _, s := range a.params.Sort() {
|
||||||
codeCursor := a.params.Cursor().Code()
|
codeCursor := a.params.Cursor().Code()
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,36 @@ func testVersionRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "OK: codes limit=1",
|
||||||
|
params: func(t *testing.T) domain.ListVersionsParams {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
params := domain.NewListVersionsParams()
|
||||||
|
require.NoError(t, params.SetLimit(1))
|
||||||
|
|
||||||
|
res, err := repos.version.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, res)
|
||||||
|
|
||||||
|
require.NoError(t, params.SetCodes([]string{res.Versions()[0].Code()}))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.ListVersionsParams, res domain.ListVersionsResult) {
|
||||||
|
t.Helper()
|
||||||
|
versions := res.Versions()
|
||||||
|
codes := params.Codes()
|
||||||
|
assert.Len(t, versions, len(codes))
|
||||||
|
for _, v := range versions {
|
||||||
|
assert.Contains(t, codes, v.Code())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "OK: limit=5",
|
name: "OK: limit=5",
|
||||||
params: func(t *testing.T) domain.ListVersionsParams {
|
params: func(t *testing.T) domain.ListVersionsParams {
|
||||||
|
|
|
@ -24,3 +24,24 @@ func (svc *VersionService) List(
|
||||||
) (domain.ListVersionsResult, error) {
|
) (domain.ListVersionsResult, error) {
|
||||||
return svc.repo.List(ctx, params)
|
return svc.repo.List(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (svc *VersionService) Get(ctx context.Context, code string) (domain.Version, error) {
|
||||||
|
params := domain.NewListVersionsParams()
|
||||||
|
if err := params.SetCodes([]string{code}); err != nil {
|
||||||
|
return domain.Version{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := svc.repo.List(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Version{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
versions := res.Versions()
|
||||||
|
if len(versions) == 0 {
|
||||||
|
return domain.Version{}, domain.VersionNotFoundError{
|
||||||
|
VersionCode: code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions[0], nil
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -172,6 +173,7 @@ func (vc VersionCursor) Encode() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListVersionsParams struct {
|
type ListVersionsParams struct {
|
||||||
|
codes []string
|
||||||
sort []VersionSort
|
sort []VersionSort
|
||||||
cursor VersionCursor
|
cursor VersionCursor
|
||||||
limit int
|
limit int
|
||||||
|
@ -189,6 +191,15 @@ func NewListVersionsParams() ListVersionsParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (params *ListVersionsParams) Codes() []string {
|
||||||
|
return params.codes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListVersionsParams) SetCodes(codes []string) error {
|
||||||
|
params.codes = codes
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
versionSortMinLength = 1
|
versionSortMinLength = 1
|
||||||
versionSortMaxLength = 1
|
versionSortMaxLength = 1
|
||||||
|
@ -308,3 +319,25 @@ func (res ListVersionsResult) Self() VersionCursor {
|
||||||
func (res ListVersionsResult) Next() VersionCursor {
|
func (res ListVersionsResult) Next() VersionCursor {
|
||||||
return res.next
|
return res.next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VersionNotFoundError struct {
|
||||||
|
VersionCode string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e VersionNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("version with code %s not found", e.VersionCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e VersionNotFoundError) Code() ErrorCode {
|
||||||
|
return ErrorCodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e VersionNotFoundError) Slug() string {
|
||||||
|
return "version-not-found"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e VersionNotFoundError) Params() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"Code": e.VersionCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,16 @@ func (h *apiHTTPHandler) ListVersions(w http.ResponseWriter, r *http.Request, pa
|
||||||
renderJSON(w, r, http.StatusOK, apimodel.NewListVersionsResponse(res))
|
renderJSON(w, r, http.StatusOK, apimodel.NewListVersionsResponse(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *apiHTTPHandler) GetVersion(w http.ResponseWriter, r *http.Request, versionCode apimodel.VersionCodePathParam) {
|
||||||
|
version, err := h.versionSvc.Get(r.Context(), versionCode)
|
||||||
|
if err != nil {
|
||||||
|
apiErrorRenderer{errors: []error{err}}.render(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderJSON(w, r, http.StatusOK, apimodel.NewGetVersionResponse(version))
|
||||||
|
}
|
||||||
|
|
||||||
func formatListVersionsParamsErrorPath(elems []errorPathElement) []string {
|
func formatListVersionsParamsErrorPath(elems []errorPathElement) []string {
|
||||||
if elems[0].model != "ListVersionsParams" {
|
if elems[0].model != "ListVersionsParams" {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -17,14 +17,22 @@ func NewListVersionsResponse(res domain.ListVersionsResult) ListVersionsResponse
|
||||||
versions := res.Versions()
|
versions := res.Versions()
|
||||||
|
|
||||||
resp := ListVersionsResponse{
|
resp := ListVersionsResponse{
|
||||||
Items: make([]Version, 0, len(versions)),
|
Data: make([]Version, 0, len(versions)),
|
||||||
Self: res.Self().Encode(),
|
Cursor: Cursor{
|
||||||
Next: res.Next().Encode(),
|
Next: res.Next().Encode(),
|
||||||
|
Self: res.Self().Encode(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range versions {
|
for _, v := range versions {
|
||||||
resp.Items = append(resp.Items, NewVersion(v))
|
resp.Data = append(resp.Data, NewVersion(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewGetVersionResponse(v domain.Version) GetVersionResponse {
|
||||||
|
return GetVersionResponse{
|
||||||
|
Data: NewVersion(v),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue