feat: api - add a new endpoint - GET /api/v2/versions/{versionCode}/servers/{serverKey}/tribes
This commit is contained in:
parent
25efaacc01
commit
cde03857a2
130
api/openapi3.yml
130
api/openapi3.yml
|
@ -12,6 +12,7 @@ info:
|
||||||
tags:
|
tags:
|
||||||
- name: versions
|
- name: versions
|
||||||
- name: servers
|
- name: servers
|
||||||
|
- name: tribes
|
||||||
servers:
|
servers:
|
||||||
- url: "{scheme}://{hostname}/api"
|
- url: "{scheme}://{hostname}/api"
|
||||||
variables:
|
variables:
|
||||||
|
@ -127,6 +128,26 @@ paths:
|
||||||
$ref: "#/components/responses/GetUnitInfoResponse"
|
$ref: "#/components/responses/GetUnitInfoResponse"
|
||||||
default:
|
default:
|
||||||
$ref: "#/components/responses/ErrorResponse"
|
$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:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
Error:
|
Error:
|
||||||
|
@ -165,6 +186,7 @@ components:
|
||||||
example: pl
|
example: pl
|
||||||
host:
|
host:
|
||||||
type: string
|
type: string
|
||||||
|
format: hostname
|
||||||
example: www.tribalwars.net
|
example: www.tribalwars.net
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
@ -193,6 +215,7 @@ components:
|
||||||
type: boolean
|
type: boolean
|
||||||
url:
|
url:
|
||||||
type: string
|
type: string
|
||||||
|
format: uri
|
||||||
example: https://pl151.plemiona.pl
|
example: https://pl151.plemiona.pl
|
||||||
numPlayers:
|
numPlayers:
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -790,6 +813,88 @@ components:
|
||||||
$ref: "#/components/schemas/Building"
|
$ref: "#/components/schemas/Building"
|
||||||
wood:
|
wood:
|
||||||
$ref: "#/components/schemas/Building"
|
$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:
|
Cursor:
|
||||||
type: object
|
type: object
|
||||||
x-go-type-skip-optional-pointer: true
|
x-go-type-skip-optional-pointer: true
|
||||||
|
@ -819,6 +924,7 @@ components:
|
||||||
name: limit
|
name: limit
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
|
minimum: 0
|
||||||
required: false
|
required: false
|
||||||
ServerOpenQueryParam:
|
ServerOpenQueryParam:
|
||||||
name: open
|
name: open
|
||||||
|
@ -828,6 +934,14 @@ components:
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
required: false
|
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:
|
VersionCodePathParam:
|
||||||
in: path
|
in: path
|
||||||
name: versionCode
|
name: versionCode
|
||||||
|
@ -926,6 +1040,22 @@ components:
|
||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
$ref: "#/components/schemas/BuildingInfo"
|
$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:
|
ErrorResponse:
|
||||||
description: Default error response.
|
description: Default error response.
|
||||||
content:
|
content:
|
||||||
|
|
|
@ -106,10 +106,12 @@ var cmdServe = &cli.Command{
|
||||||
// adapters
|
// adapters
|
||||||
versionRepo := adapter.NewVersionBunRepository(bunDB)
|
versionRepo := adapter.NewVersionBunRepository(bunDB)
|
||||||
serverRepo := adapter.NewServerBunRepository(bunDB)
|
serverRepo := adapter.NewServerBunRepository(bunDB)
|
||||||
|
tribeRepo := adapter.NewTribeBunRepository(bunDB)
|
||||||
|
|
||||||
// services
|
// services
|
||||||
versionSvc := app.NewVersionService(versionRepo)
|
versionSvc := app.NewVersionService(versionRepo)
|
||||||
serverSvc := app.NewServerService(serverRepo, nil, nil)
|
serverSvc := app.NewServerService(serverRepo, nil, nil)
|
||||||
|
tribeSvc := app.NewTribeService(tribeRepo, nil, nil)
|
||||||
|
|
||||||
// health
|
// health
|
||||||
h := health.New()
|
h := health.New()
|
||||||
|
@ -135,6 +137,7 @@ var cmdServe = &cli.Command{
|
||||||
r.Mount(apiBasePath, port.NewAPIHTTPHandler(
|
r.Mount(apiBasePath, port.NewAPIHTTPHandler(
|
||||||
versionSvc,
|
versionSvc,
|
||||||
serverSvc,
|
serverSvc,
|
||||||
|
tribeSvc,
|
||||||
port.WithOpenAPIConfig(oapiCfg),
|
port.WithOpenAPIConfig(oapiCfg),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
type apiHTTPHandler struct {
|
type apiHTTPHandler struct {
|
||||||
versionSvc *app.VersionService
|
versionSvc *app.VersionService
|
||||||
serverSvc *app.ServerService
|
serverSvc *app.ServerService
|
||||||
|
tribeSvc *app.TribeService
|
||||||
openAPISchema func() (*openapi3.T, error)
|
openAPISchema func() (*openapi3.T, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ func WithOpenAPIConfig(oapiCfg OpenAPIConfig) APIHTTPHandlerOption {
|
||||||
func NewAPIHTTPHandler(
|
func NewAPIHTTPHandler(
|
||||||
versionSvc *app.VersionService,
|
versionSvc *app.VersionService,
|
||||||
serverSvc *app.ServerService,
|
serverSvc *app.ServerService,
|
||||||
|
tribeSvc *app.TribeService,
|
||||||
opts ...APIHTTPHandlerOption,
|
opts ...APIHTTPHandlerOption,
|
||||||
) http.Handler {
|
) http.Handler {
|
||||||
cfg := newAPIHTTPHandlerConfig(opts...)
|
cfg := newAPIHTTPHandlerConfig(opts...)
|
||||||
|
@ -34,6 +36,7 @@ func NewAPIHTTPHandler(
|
||||||
h := &apiHTTPHandler{
|
h := &apiHTTPHandler{
|
||||||
versionSvc: versionSvc,
|
versionSvc: versionSvc,
|
||||||
serverSvc: serverSvc,
|
serverSvc: serverSvc,
|
||||||
|
tribeSvc: tribeSvc,
|
||||||
openAPISchema: sync.OnceValues(func() (*openapi3.T, error) {
|
openAPISchema: sync.OnceValues(func() (*openapi3.T, error) {
|
||||||
return getOpenAPISchema(cfg.openAPI)
|
return getOpenAPISchema(cfg.openAPI)
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -152,8 +152,6 @@ func formatListServersParamsErrorPath(segments []errorPathSegment) []string {
|
||||||
return []string{"$query", "open"}
|
return []string{"$query", "open"}
|
||||||
case "sort":
|
case "sort":
|
||||||
return []string{"$query", "sort"}
|
return []string{"$query", "sort"}
|
||||||
case "versionCodes":
|
|
||||||
return []string{"$path", "versionCode"}
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,12 @@ func newAPIHTTPHandler(tb testing.TB, opts ...func(cfg *apiHTTPHandlerConfig)) h
|
||||||
|
|
||||||
versionRepo := adapter.NewVersionBunRepository(db)
|
versionRepo := adapter.NewVersionBunRepository(db)
|
||||||
serverRepo := adapter.NewServerBunRepository(db)
|
serverRepo := adapter.NewServerBunRepository(db)
|
||||||
|
tribeRepo := adapter.NewTribeBunRepository(db)
|
||||||
|
|
||||||
return port.NewAPIHTTPHandler(
|
return port.NewAPIHTTPHandler(
|
||||||
app.NewVersionService(versionRepo),
|
app.NewVersionService(versionRepo),
|
||||||
app.NewServerService(serverRepo, nil, nil),
|
app.NewServerService(serverRepo, nil, nil),
|
||||||
|
app.NewTribeService(tribeRepo, nil, nil),
|
||||||
cfg.options...,
|
cfg.options...,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue