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

This commit is contained in:
Dawid Wysokiński 2024-02-20 08:14:35 +01:00
parent 4ce2131ac4
commit 30be8bebc0
Signed by: Kichiyaki
GPG Key ID: B5445E357FB8B892
6 changed files with 172 additions and 2 deletions

View File

@ -149,7 +149,23 @@ paths:
$ref: "#/components/responses/ListTribesResponse"
default:
$ref: "#/components/responses/ErrorResponse"
/v2/versions/{versionCode}/servers/{serverKey}/tribes/{tribeId}:
get:
operationId: getTribe
tags:
- versions
- servers
- tribes
description: Get a tribe
parameters:
- $ref: "#/components/parameters/VersionCodePathParam"
- $ref: "#/components/parameters/ServerKeyPathParam"
- $ref: "#/components/parameters/TribeIdPathParam"
responses:
200:
$ref: "#/components/responses/GetTribeResponse"
default:
$ref: "#/components/responses/ErrorResponse"
components:
schemas:
Error:
@ -993,6 +1009,13 @@ components:
required: true
schema:
type: string
TribeIdPathParam:
in: path
name: tribeId
required: true
schema:
type: integer
minimum: 0
responses:
ListVersionsResponse:
description: ""
@ -1094,7 +1117,17 @@ components:
type: array
items:
$ref: "#/components/schemas/Tribe"
GetTribeResponse:
description: ""
content:
application/json:
schema:
type: object
required:
- data
properties:
data:
$ref: "#/components/schemas/Tribe"
ErrorResponse:
description: Default error response.
content:

View File

@ -157,3 +157,32 @@ func (svc *TribeService) UpdateDominance(ctx context.Context, payload domain.Vil
func (svc *TribeService) List(ctx context.Context, params domain.ListTribesParams) (domain.ListTribesResult, error) {
return svc.repo.List(ctx, params)
}
func (svc *TribeService) Get(
ctx context.Context,
id int,
serverKey string,
) (domain.Tribe, error) {
params := domain.NewListTribesParams()
if err := params.SetIDs([]int{id}); err != nil {
return domain.Tribe{}, err
}
if err := params.SetServerKeys([]string{serverKey}); err != nil {
return domain.Tribe{}, err
}
res, err := svc.repo.List(ctx, params)
if err != nil {
return domain.Tribe{}, err
}
tribes := res.Tribes()
if len(tribes) == 0 {
return domain.Tribe{}, domain.TribeNotFoundError{
ID: id,
ServerKey: serverKey,
}
}
return tribes[0], nil
}

View File

@ -862,3 +862,29 @@ func (res ListTribesResult) Self() TribeCursor {
func (res ListTribesResult) Next() TribeCursor {
return res.next
}
type TribeNotFoundError struct {
ID int
ServerKey string
}
var _ ErrorWithParams = TribeNotFoundError{}
func (e TribeNotFoundError) Error() string {
return fmt.Sprintf("tribe with id %d and server key %s not found", e.ID, e.ServerKey)
}
func (e TribeNotFoundError) Type() ErrorType {
return ErrorTypeNotFound
}
func (e TribeNotFoundError) Code() string {
return "tribe-not-found"
}
func (e TribeNotFoundError) Params() map[string]any {
return map[string]any{
"ID": e.ID,
"ServerKey": e.ServerKey,
}
}

View File

@ -70,6 +70,7 @@ func NewAPIHTTPHandler(
middleware.NoCache,
h.versionMiddleware,
h.serverMiddleware,
h.tribeMiddleware,
},
ErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
apiErrorRenderer{errors: []error{err}}.render(w, r)

View File

@ -1,11 +1,15 @@
package port
import (
"context"
"fmt"
"net/http"
"slices"
"strconv"
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
"gitea.dwysokinski.me/twhelp/corev3/internal/port/internal/apimodel"
"github.com/go-chi/chi/v5"
)
//nolint:gocyclo
@ -75,6 +79,60 @@ func (h *apiHTTPHandler) ListTribes(
renderJSON(w, r, http.StatusOK, apimodel.NewListTribesResponse(res))
}
func (h *apiHTTPHandler) GetTribe(
w http.ResponseWriter,
r *http.Request,
_ apimodel.VersionCodePathParam,
_ apimodel.ServerKeyPathParam,
_ apimodel.TribeIdPathParam,
) {
tribe, _ := tribeFromContext(r.Context())
renderJSON(w, r, http.StatusOK, apimodel.NewGetTribeResponse(tribe))
}
func (h *apiHTTPHandler) tribeMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
routeCtx := chi.RouteContext(ctx)
server, serverOK := serverFromContext(ctx)
tribeIDIdx := slices.Index(routeCtx.URLParams.Keys, "tribeId")
if !serverOK || tribeIDIdx < 0 {
next.ServeHTTP(w, r)
return
}
tribeID, err := strconv.Atoi(routeCtx.URLParams.Values[tribeIDIdx])
if err != nil {
return
}
tribe, err := h.tribeSvc.Get(
ctx,
tribeID,
server.Key(),
)
if err != nil {
fmt.Println(err)
apiErrorRenderer{errors: []error{err}, formatErrorPath: formatGetTribeErrorPath}.render(w, r)
return
}
next.ServeHTTP(w, r.WithContext(tribeToContext(ctx, tribe)))
})
}
type tribeCtxKey struct{}
func tribeToContext(ctx context.Context, t domain.Tribe) context.Context {
return context.WithValue(ctx, tribeCtxKey{}, t)
}
func tribeFromContext(ctx context.Context) (domain.Tribe, bool) {
t, ok := ctx.Value(tribeCtxKey{}).(domain.Tribe)
return t, ok
}
func formatListTribesParamsErrorPath(segments []errorPathSegment) []string {
if segments[0].model != "ListTribesParams" {
return nil
@ -97,3 +155,20 @@ func formatListTribesParamsErrorPath(segments []errorPathSegment) []string {
return nil
}
}
func formatGetTribeErrorPath(segments []errorPathSegment) []string {
if segments[0].model != "ListTribesParams" {
return nil
}
switch segments[0].field {
case "ids":
path := []string{"$path", "ids"}
if segments[0].index >= 0 {
path = append(path, strconv.Itoa(segments[0].index))
}
return path
default:
return nil
}
}

View File

@ -61,3 +61,9 @@ func NewListTribesResponse(res domain.ListTribesResult) ListTribesResponse {
return resp
}
func NewGetTribeResponse(t domain.Tribe) GetTribeResponse {
return GetTribeResponse{
Data: NewTribe(t),
}
}