feat: api - add a new endpoint - GET /api/v2/versions/{versionCode}/servers/{serverKey}/tribes

This commit is contained in:
Dawid Wysokiński 2024-02-14 07:43:55 +01:00
parent 25efaacc01
commit cde03857a2
Signed by: Kichiyaki
GPG Key ID: B5445E357FB8B892
7 changed files with 274 additions and 2 deletions

View File

@ -12,6 +12,7 @@ info:
tags:
- name: versions
- name: servers
- name: tribes
servers:
- url: "{scheme}://{hostname}/api"
variables:
@ -127,6 +128,26 @@ paths:
$ref: "#/components/responses/GetUnitInfoResponse"
default:
$ref: "#/components/responses/ErrorResponse"
/v2/versions/{versionCode}/servers/{serverKey}/tribes:
get:
operationId: listTribes
tags:
- versions
- servers
- tribes
description: List tribes
parameters:
- $ref: "#/components/parameters/VersionCodePathParam"
- $ref: "#/components/parameters/ServerKeyPathParam"
- $ref: "#/components/parameters/CursorQueryParam"
- $ref: "#/components/parameters/LimitQueryParam"
- $ref: "#/components/parameters/TribeDeletedQueryParam"
responses:
200:
$ref: "#/components/responses/ListTribesResponse"
default:
$ref: "#/components/responses/ErrorResponse"
components:
schemas:
Error:
@ -165,6 +186,7 @@ components:
example: pl
host:
type: string
format: hostname
example: www.tribalwars.net
name:
type: string
@ -193,6 +215,7 @@ components:
type: boolean
url:
type: string
format: uri
example: https://pl151.plemiona.pl
numPlayers:
type: integer
@ -790,6 +813,88 @@ components:
$ref: "#/components/schemas/Building"
wood:
$ref: "#/components/schemas/Building"
Tribe:
type: object
required:
- id
- name
- tag
- profileUrl
- points
- allPoints
- numMembers
- numVillages
- rank
- dominance
- rankAtt
- scoreAtt
- rankDef
- scoreDef
- rankTotal
- scoreTotal
- bestRank
- bestRankAt
- mostVillages
- mostVillagesAt
- mostPoints
- mostPointsAt
- createdAt
properties:
id:
type: integer
name:
type: string
tag:
type: string
profileUrl:
type: string
format: uri
points:
type: integer
allPoints:
type: integer
numMembers:
type: integer
numVillages:
type: integer
rank:
type: integer
dominance:
type: number
format: double
rankAtt:
type: integer
scoreAtt:
type: integer
rankDef:
type: integer
scoreDef:
type: integer
rankTotal:
type: integer
scoreTotal:
type: integer
bestRank:
type: integer
bestRankAt:
type: string
format: date-time
mostVillages:
type: integer
mostVillagesAt:
type: string
format: date-time
mostPoints:
type: integer
mostPointsAt:
type: string
format: date-time
createdAt:
type: string
format: date-time
deletedAt:
type: string
format: date-time
Cursor:
type: object
x-go-type-skip-optional-pointer: true
@ -819,6 +924,7 @@ components:
name: limit
schema:
type: integer
minimum: 0
required: false
ServerOpenQueryParam:
name: open
@ -828,6 +934,14 @@ components:
schema:
type: boolean
required: false
TribeDeletedQueryParam:
name: deleted
in: query
description: true=only deleted tribes, false=only existing tribes,
by default both existing and deleted tribes are returned
schema:
type: boolean
required: false
VersionCodePathParam:
in: path
name: versionCode
@ -926,6 +1040,22 @@ components:
properties:
data:
$ref: "#/components/schemas/BuildingInfo"
ListTribesResponse:
description: ""
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PaginationResponse"
- type: object
required:
- data
properties:
data:
type: array
items:
$ref: "#/components/schemas/Tribe"
ErrorResponse:
description: Default error response.
content:

View File

@ -106,10 +106,12 @@ var cmdServe = &cli.Command{
// adapters
versionRepo := adapter.NewVersionBunRepository(bunDB)
serverRepo := adapter.NewServerBunRepository(bunDB)
tribeRepo := adapter.NewTribeBunRepository(bunDB)
// services
versionSvc := app.NewVersionService(versionRepo)
serverSvc := app.NewServerService(serverRepo, nil, nil)
tribeSvc := app.NewTribeService(tribeRepo, nil, nil)
// health
h := health.New()
@ -135,6 +137,7 @@ var cmdServe = &cli.Command{
r.Mount(apiBasePath, port.NewAPIHTTPHandler(
versionSvc,
serverSvc,
tribeSvc,
port.WithOpenAPIConfig(oapiCfg),
))

View File

@ -15,6 +15,7 @@ import (
type apiHTTPHandler struct {
versionSvc *app.VersionService
serverSvc *app.ServerService
tribeSvc *app.TribeService
openAPISchema func() (*openapi3.T, error)
}
@ -27,6 +28,7 @@ func WithOpenAPIConfig(oapiCfg OpenAPIConfig) APIHTTPHandlerOption {
func NewAPIHTTPHandler(
versionSvc *app.VersionService,
serverSvc *app.ServerService,
tribeSvc *app.TribeService,
opts ...APIHTTPHandlerOption,
) http.Handler {
cfg := newAPIHTTPHandlerConfig(opts...)
@ -34,6 +36,7 @@ func NewAPIHTTPHandler(
h := &apiHTTPHandler{
versionSvc: versionSvc,
serverSvc: serverSvc,
tribeSvc: tribeSvc,
openAPISchema: sync.OnceValues(func() (*openapi3.T, error) {
return getOpenAPISchema(cfg.openAPI)
}),

View File

@ -152,8 +152,6 @@ func formatListServersParamsErrorPath(segments []errorPathSegment) []string {
return []string{"$query", "open"}
case "sort":
return []string{"$query", "sort"}
case "versionCodes":
return []string{"$path", "versionCode"}
default:
return nil
}

View File

@ -29,10 +29,12 @@ func newAPIHTTPHandler(tb testing.TB, opts ...func(cfg *apiHTTPHandlerConfig)) h
versionRepo := adapter.NewVersionBunRepository(db)
serverRepo := adapter.NewServerBunRepository(db)
tribeRepo := adapter.NewTribeBunRepository(db)
return port.NewAPIHTTPHandler(
app.NewVersionService(versionRepo),
app.NewServerService(serverRepo, nil, nil),
app.NewTribeService(tribeRepo, nil, nil),
cfg.options...,
)
}

View File

@ -0,0 +1,79 @@
package port
import (
"net/http"
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
"gitea.dwysokinski.me/twhelp/corev3/internal/port/internal/apimodel"
)
func (h *apiHTTPHandler) ListTribes(
w http.ResponseWriter,
r *http.Request,
_ apimodel.VersionCodePathParam,
serverKey apimodel.ServerKeyPathParam,
params apimodel.ListTribesParams,
) {
domainParams := domain.NewListTribesParams()
if err := domainParams.SetSort([]domain.TribeSort{domain.TribeSortIDASC}); err != nil {
apiErrorRenderer{errors: []error{err}, formatErrorPath: formatListTribesParamsErrorPath}.render(w, r)
return
}
if err := domainParams.SetServerKeys([]string{serverKey}); 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,
Valid: true,
}); err != nil {
apiErrorRenderer{errors: []error{err}, formatErrorPath: formatListTribesParamsErrorPath}.render(w, r)
return
}
}
if params.Limit != nil {
if err := domainParams.SetLimit(*params.Limit); err != nil {
apiErrorRenderer{errors: []error{err}, formatErrorPath: formatListTribesParamsErrorPath}.render(w, r)
return
}
}
if params.Cursor != nil {
if err := domainParams.SetEncodedCursor(*params.Cursor); err != nil {
apiErrorRenderer{errors: []error{err}, formatErrorPath: formatListTribesParamsErrorPath}.render(w, r)
return
}
}
res, err := h.tribeSvc.List(r.Context(), domainParams)
if err != nil {
apiErrorRenderer{errors: []error{err}}.render(w, r)
return
}
renderJSON(w, r, http.StatusOK, apimodel.NewListTribesResponse(res))
}
func formatListTribesParamsErrorPath(segments []errorPathSegment) []string {
if segments[0].model != "ListTribesParams" {
return nil
}
switch segments[0].field {
case "cursor":
return []string{"$query", "cursor"}
case "limit":
return []string{"$query", "limit"}
case "deleted":
return []string{"$query", "deleted"}
case "sort":
return []string{"$query", "sort"}
default:
return nil
}
}

View File

@ -0,0 +1,57 @@
package apimodel
import (
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
)
func NewTribe(t domain.Tribe) Tribe {
converted := Tribe{
AllPoints: t.AllPoints(),
BestRank: t.BestRank(),
BestRankAt: t.BestRankAt(),
CreatedAt: t.CreatedAt(),
Dominance: t.Dominance(),
Id: t.ID(),
MostPoints: t.MostPoints(),
MostPointsAt: t.MostPointsAt(),
MostVillages: t.MostVillages(),
MostVillagesAt: t.MostVillagesAt(),
Name: t.Name(),
NumMembers: t.NumMembers(),
NumVillages: t.NumVillages(),
Points: t.Points(),
ProfileUrl: t.ProfileURL().String(),
Rank: t.Rank(),
RankAtt: t.OD().RankAtt(),
RankDef: t.OD().RankDef(),
RankTotal: t.OD().RankTotal(),
ScoreAtt: t.OD().ScoreAtt(),
ScoreDef: t.OD().ScoreDef(),
ScoreTotal: t.OD().ScoreTotal(),
Tag: t.Tag(),
}
if deletedAt := t.DeletedAt(); !deletedAt.IsZero() {
converted.DeletedAt = &deletedAt
}
return converted
}
func NewListTribesResponse(res domain.ListTribesResult) ListTribesResponse {
tribes := res.Tribes()
resp := ListTribesResponse{
Data: make([]Tribe, 0, len(tribes)),
Cursor: Cursor{
Next: res.Next().Encode(),
Self: res.Self().Encode(),
},
}
for _, t := range tribes {
resp.Data = append(resp.Data, NewTribe(t))
}
return resp
}