From 9c994ee22b38fce8acdbf950da56e043ccbdf269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Wysoki=C5=84ski?= Date: Mon, 13 May 2024 06:59:57 +0200 Subject: [PATCH] feat: new endpoint GET /api/v2/versions/{versionCode}/servers/{serverKey}/snapshots --- api/openapi3.yml | 116 +++- cmd/twhelp/cmd_serve.go | 3 + .../adapter/repository_bun_server_snapshot.go | 27 + .../repository_server_snapshot_test.go | 10 +- internal/adapter/repository_test.go | 4 + internal/app/service_server_snapshot.go | 12 +- internal/bun/bunmodel/server_snapshot.go | 19 + internal/domain/domaintest/server_snapshot.go | 35 ++ internal/domain/server_snapshot.go | 80 +++ internal/domain/server_snapshot_test.go | 47 ++ internal/port/handler_http_api.go | 3 + .../port/handler_http_api_server_snapshot.go | 96 ++++ .../handler_http_api_server_snapshot_test.go | 542 ++++++++++++++++++ internal/port/handler_http_api_test.go | 2 + .../port/internal/apimodel/server_snapshot.go | 43 ++ internal/port/testdata/api/fixture.yml | 58 ++ .../testdata/snapshotcreation/fixture.yml | 4 + k8s/base/api.yml | 2 - k8s/overlays/prod/api.yml | 2 + 19 files changed, 1090 insertions(+), 15 deletions(-) create mode 100644 internal/port/handler_http_api_server_snapshot.go create mode 100644 internal/port/handler_http_api_server_snapshot_test.go create mode 100644 internal/port/internal/apimodel/server_snapshot.go diff --git a/api/openapi3.yml b/api/openapi3.yml index 744556a..12ec938 100644 --- a/api/openapi3.yml +++ b/api/openapi3.yml @@ -471,6 +471,25 @@ paths: $ref: "#/components/responses/ListTribeChangesResponse" default: $ref: "#/components/responses/ErrorResponse" + /v2/versions/{versionCode}/servers/{serverKey}/snapshots: + get: + operationId: listServerServerSnapshots + tags: + - versions + - servers + - snapshots + description: List the given server's snapshots + parameters: + - $ref: "#/components/parameters/VersionCodePathParam" + - $ref: "#/components/parameters/ServerKeyPathParam" + - $ref: "#/components/parameters/CursorQueryParam" + - $ref: "#/components/parameters/LimitQueryParam" + - $ref: "#/components/parameters/ServerSnapshotSortQueryParam" + responses: + 200: + $ref: "#/components/responses/ListServerSnapshotsResponse" + default: + $ref: "#/components/responses/ErrorResponse" /v2/versions/{versionCode}/servers/{serverKey}/tribes/{tribeId}/snapshots: get: operationId: listTribeTribeSnapshots @@ -1594,6 +1613,52 @@ components: createdAt: type: string format: date-time + ServerSnapshot: + type: object + required: + - id + - server + - numPlayers + - numActivePlayers + - numInactivePlayers + - numTribes + - numActiveTribes + - numInactiveTribes + - numVillages + - numBarbarianVillages + - numBonusVillages + - numPlayerVillages + - date + properties: + id: + $ref: "#/components/schemas/IntId" + server: + $ref: "#/components/schemas/ServerMeta" + numPlayers: + type: integer + description: numActivePlayers+numInactivePlayers + numActivePlayers: + type: integer + numInactivePlayers: + type: integer + numTribes: + type: integer + description: numActiveTribes+numInactiveTribes + numActiveTribes: + type: integer + numInactiveTribes: + type: integer + numVillages: + type: integer + numBarbarianVillages: + type: integer + numBonusVillages: + type: integer + numPlayerVillages: + type: integer + date: + type: string + format: date TribeSnapshot: type: object required: @@ -1675,7 +1740,7 @@ components: x-go-type-skip-optional-pointer: true allOf: - $ref: "#/components/schemas/CursorString" - PaginationResponse: + Pagination: type: object properties: cursor: @@ -1840,6 +1905,20 @@ components: - date:ASC - date:DESC maxItems: 1 + ServerSnapshotSortQueryParam: + name: sort + in: query + description: Order matters! + schema: + type: array + default: + - date:ASC + items: + type: string + enum: + - date:ASC + - date:DESC + maxItems: 1 PlayerSnapshotSortQueryParam: name: sort in: query @@ -1905,7 +1984,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -1931,7 +2010,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -1990,7 +2069,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -2016,7 +2095,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -2031,7 +2110,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -2057,7 +2136,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -2083,7 +2162,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -2098,7 +2177,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -2107,13 +2186,28 @@ components: type: array items: $ref: "#/components/schemas/TribeChange" + ListServerSnapshotsResponse: + description: "" + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Pagination" + - type: object + required: + - data + properties: + data: + type: array + items: + $ref: "#/components/schemas/ServerSnapshot" ListTribeSnapshotsResponse: description: "" content: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data @@ -2128,7 +2222,7 @@ components: application/json: schema: allOf: - - $ref: "#/components/schemas/PaginationResponse" + - $ref: "#/components/schemas/Pagination" - type: object required: - data diff --git a/cmd/twhelp/cmd_serve.go b/cmd/twhelp/cmd_serve.go index 01256af..7a3af2b 100644 --- a/cmd/twhelp/cmd_serve.go +++ b/cmd/twhelp/cmd_serve.go @@ -113,6 +113,7 @@ var cmdServe = &cli.Command{ villageRepo := adapter.NewVillageBunRepository(bunDB) ennoblementRepo := adapter.NewEnnoblementBunRepository(bunDB) tribeChangeRepo := adapter.NewTribeChangeBunRepository(bunDB) + serverSnapshotRepo := adapter.NewServerSnapshotBunRepository(bunDB) tribeSnapshotRepo := adapter.NewTribeSnapshotBunRepository(bunDB) playerSnapshotRepo := adapter.NewPlayerSnapshotBunRepository(bunDB) @@ -124,6 +125,7 @@ var cmdServe = &cli.Command{ playerSvc := app.NewPlayerService(playerRepo, tribeChangeSvc, nil, nil) villageSvc := app.NewVillageService(villageRepo, nil, nil) ennoblementSvc := app.NewEnnoblementService(ennoblementRepo, nil, nil) + serverSnapshotSvc := app.NewServerSnapshotService(serverSnapshotRepo, serverSvc, nil) tribeSnapshotSvc := app.NewTribeSnapshotService(tribeSnapshotRepo, tribeSvc, nil) playerSnapshotSvc := app.NewPlayerSnapshotService(playerSnapshotRepo, playerSvc, nil) @@ -160,6 +162,7 @@ var cmdServe = &cli.Command{ villageSvc, ennoblementSvc, tribeChangeSvc, + serverSnapshotSvc, tribeSnapshotSvc, playerSnapshotSvc, port.WithOpenAPIConfig(oapiCfg), diff --git a/internal/adapter/repository_bun_server_snapshot.go b/internal/adapter/repository_bun_server_snapshot.go index 9be39c6..2051661 100644 --- a/internal/adapter/repository_bun_server_snapshot.go +++ b/internal/adapter/repository_bun_server_snapshot.go @@ -81,6 +81,33 @@ func (repo *ServerSnapshotBunRepository) List( return domain.NewListServerSnapshotsResult(separateListResultAndNext(converted, params.Limit())) } +func (repo *ServerSnapshotBunRepository) ListWithRelations( + ctx context.Context, + params domain.ListServerSnapshotsParams, +) (domain.ListServerSnapshotsWithRelationsResult, error) { + var serverSnapshots bunmodel.ServerSnapshots + + if err := repo.db.NewSelect(). + Model(&serverSnapshots). + Apply(listServerSnapshotsParamsApplier{params: params}.apply). + Relation("Server", func(q *bun.SelectQuery) *bun.SelectQuery { + return q.Column(bunmodel.ServerMetaColumns...) + }). + Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) { + return domain.ListServerSnapshotsWithRelationsResult{}, fmt.Errorf( + "couldn't select server snapshots from the db: %w", + err, + ) + } + + converted, err := serverSnapshots.ToDomainWithRelations() + if err != nil { + return domain.ListServerSnapshotsWithRelationsResult{}, err + } + + return domain.NewListServerSnapshotsWithRelationsResult(separateListResultAndNext(converted, params.Limit())) +} + func (repo *ServerSnapshotBunRepository) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error { if _, err := repo.db.NewDelete(). Model((*bunmodel.ServerSnapshot)(nil)). diff --git a/internal/adapter/repository_server_snapshot_test.go b/internal/adapter/repository_server_snapshot_test.go index a0ff482..414d3ab 100644 --- a/internal/adapter/repository_server_snapshot_test.go +++ b/internal/adapter/repository_server_snapshot_test.go @@ -115,7 +115,7 @@ func testServerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo }) }) - t.Run("List", func(t *testing.T) { + t.Run("List & ListWithRelations", func(t *testing.T) { t.Parallel() repos := newRepos(t) @@ -405,6 +405,14 @@ func testServerSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repo res, err := repos.serverSnapshot.List(ctx, params) assertError(t, err) tt.assertResult(t, params, res) + + resWithRelations, err := repos.serverSnapshot.ListWithRelations(ctx, params) + assertError(t, err) + require.Len(t, resWithRelations.ServerSnapshots(), len(res.ServerSnapshots())) + for i, ss := range resWithRelations.ServerSnapshots() { + assert.Equal(t, res.ServerSnapshots()[i], ss.ServerSnapshot()) + assert.Equal(t, ss.ServerSnapshot().ServerKey(), ss.Server().Key()) + } }) } }) diff --git a/internal/adapter/repository_test.go b/internal/adapter/repository_test.go index 641f7ac..58b09cc 100644 --- a/internal/adapter/repository_test.go +++ b/internal/adapter/repository_test.go @@ -70,6 +70,10 @@ type tribeChangeRepository interface { type serverSnapshotRepository interface { Create(ctx context.Context, params ...domain.CreateServerSnapshotParams) error List(ctx context.Context, params domain.ListServerSnapshotsParams) (domain.ListServerSnapshotsResult, error) + ListWithRelations( + ctx context.Context, + params domain.ListServerSnapshotsParams, + ) (domain.ListServerSnapshotsWithRelationsResult, error) Delete(ctx context.Context, serverKey string, dateLTE time.Time) error } diff --git a/internal/app/service_server_snapshot.go b/internal/app/service_server_snapshot.go index ae96727..0fb348b 100644 --- a/internal/app/service_server_snapshot.go +++ b/internal/app/service_server_snapshot.go @@ -11,6 +11,10 @@ type ServerSnapshotRepository interface { // Create persists tribe snapshots in a store (e.g. Postgres). // Duplicates are ignored. Create(ctx context.Context, params ...domain.CreateServerSnapshotParams) error + ListWithRelations( + ctx context.Context, + params domain.ListServerSnapshotsParams, + ) (domain.ListServerSnapshotsWithRelationsResult, error) } type ServerSnapshotService struct { @@ -27,7 +31,6 @@ func NewServerSnapshotService( return &ServerSnapshotService{repo: repo, serverSvc: serverSvc, pub: pub} } -//nolint:gocyclo func (svc *ServerSnapshotService) Create( ctx context.Context, createSnapshotsCmdPayload domain.CreateSnapshotsCmdPayload, @@ -60,3 +63,10 @@ func (svc *ServerSnapshotService) Create( return nil } + +func (svc *ServerSnapshotService) ListWithRelations( + ctx context.Context, + params domain.ListServerSnapshotsParams, +) (domain.ListServerSnapshotsWithRelationsResult, error) { + return svc.repo.ListWithRelations(ctx, params) +} diff --git a/internal/bun/bunmodel/server_snapshot.go b/internal/bun/bunmodel/server_snapshot.go index c1b98da..a5f327f 100644 --- a/internal/bun/bunmodel/server_snapshot.go +++ b/internal/bun/bunmodel/server_snapshot.go @@ -13,6 +13,7 @@ type ServerSnapshot struct { ID int `bun:"id,pk,autoincrement,identity"` ServerKey string `bun:"server_key,nullzero"` + Server Server `bun:"server,rel:belongs-to,join:server_key=key"` NumPlayers int `bun:"num_players"` NumActivePlayers int `bun:"num_active_players"` NumInactivePlayers int `bun:"num_inactive_players"` @@ -55,8 +56,26 @@ func (ss ServerSnapshot) ToDomain() (domain.ServerSnapshot, error) { return converted, nil } +func (ss ServerSnapshot) ToDomainWithRelations() (domain.ServerSnapshotWithRelations, error) { + converted, err := ss.ToDomain() + if err != nil { + return domain.ServerSnapshotWithRelations{}, err + } + + server, err := ss.Server.ToMeta() + if err != nil { + return domain.ServerSnapshotWithRelations{}, err + } + + return converted.WithRelations(server), nil +} + type ServerSnapshots []ServerSnapshot func (sss ServerSnapshots) ToDomain() (domain.ServerSnapshots, error) { return sliceToDomain(sss) } + +func (sss ServerSnapshots) ToDomainWithRelations() (domain.ServerSnapshotsWithRelations, error) { + return sliceToDomainWithRelations(sss) +} diff --git a/internal/domain/domaintest/server_snapshot.go b/internal/domain/domaintest/server_snapshot.go index 99cc606..2e621ac 100644 --- a/internal/domain/domaintest/server_snapshot.go +++ b/internal/domain/domaintest/server_snapshot.go @@ -100,3 +100,38 @@ func NewServerSnapshot(tb TestingTB, opts ...func(cfg *ServerSnapshotConfig)) do return ss } + +type ServerSnapshotWithRelationsConfig struct { + ServerSnapshotOptions []func(cfg *ServerSnapshotConfig) + ServerOptions []func(cfg *ServerConfig) +} + +func NewServerSnapshotWithRelations( + tb TestingTB, + opts ...func(cfg *ServerSnapshotWithRelationsConfig), +) domain.ServerSnapshotWithRelations { + tb.Helper() + + cfg := &ServerSnapshotWithRelationsConfig{} + + for _, opt := range opts { + opt(cfg) + } + + ss := NewServerSnapshot(tb, cfg.ServerSnapshotOptions...) + + if ss.ServerKey() != "" { + cfg.ServerOptions = append([]func(cfg *ServerConfig){ + func(cfg *ServerConfig) { + cfg.Key = ss.ServerKey() + }, + }, cfg.ServerOptions...) + } + + var tribe domain.ServerMeta + if len(cfg.ServerOptions) > 0 { + tribe = NewServer(tb, cfg.ServerOptions...).Meta() + } + + return ss.WithRelations(tribe) +} diff --git a/internal/domain/server_snapshot.go b/internal/domain/server_snapshot.go index 3c243e2..c98046d 100644 --- a/internal/domain/server_snapshot.go +++ b/internal/domain/server_snapshot.go @@ -135,6 +135,13 @@ func (ss ServerSnapshot) CreatedAt() time.Time { return ss.createdAt } +func (ss ServerSnapshot) WithRelations(server ServerMeta) ServerSnapshotWithRelations { + return ServerSnapshotWithRelations{ + snapshot: ss, + server: server, + } +} + func (ss ServerSnapshot) ToCursor() (ServerSnapshotCursor, error) { return NewServerSnapshotCursor(ss.id, ss.serverKey, ss.date) } @@ -145,6 +152,25 @@ func (ss ServerSnapshot) IsZero() bool { type ServerSnapshots []ServerSnapshot +type ServerSnapshotWithRelations struct { + snapshot ServerSnapshot + server ServerMeta +} + +func (ts ServerSnapshotWithRelations) ServerSnapshot() ServerSnapshot { + return ts.snapshot +} + +func (ts ServerSnapshotWithRelations) Server() ServerMeta { + return ts.server +} + +func (ts ServerSnapshotWithRelations) IsZero() bool { + return ts.snapshot.IsZero() +} + +type ServerSnapshotsWithRelations []ServerSnapshotWithRelations + type CreateServerSnapshotParams struct { serverKey string numPlayers int @@ -576,3 +602,57 @@ func (res ListServerSnapshotsResult) Self() ServerSnapshotCursor { func (res ListServerSnapshotsResult) Next() ServerSnapshotCursor { return res.next } + +type ListServerSnapshotsWithRelationsResult struct { + snapshots ServerSnapshotsWithRelations + self ServerSnapshotCursor + next ServerSnapshotCursor +} + +const listServerSnapshotsWithRelationsResultModelName = "ListServerSnapshotsWithRelationsResult" + +func NewListServerSnapshotsWithRelationsResult( + snapshots ServerSnapshotsWithRelations, + next ServerSnapshotWithRelations, +) (ListServerSnapshotsWithRelationsResult, error) { + var err error + res := ListServerSnapshotsWithRelationsResult{ + snapshots: snapshots, + } + + if len(snapshots) > 0 { + res.self, err = snapshots[0].ServerSnapshot().ToCursor() + if err != nil { + return ListServerSnapshotsWithRelationsResult{}, ValidationError{ + Model: listServerSnapshotsWithRelationsResultModelName, + Field: "self", + Err: err, + } + } + } + + if !next.IsZero() { + res.next, err = next.ServerSnapshot().ToCursor() + if err != nil { + return ListServerSnapshotsWithRelationsResult{}, ValidationError{ + Model: listServerSnapshotsWithRelationsResultModelName, + Field: "next", + Err: err, + } + } + } + + return res, nil +} + +func (res ListServerSnapshotsWithRelationsResult) ServerSnapshots() ServerSnapshotsWithRelations { + return res.snapshots +} + +func (res ListServerSnapshotsWithRelationsResult) Self() ServerSnapshotCursor { + return res.self +} + +func (res ListServerSnapshotsWithRelationsResult) Next() ServerSnapshotCursor { + return res.next +} diff --git a/internal/domain/server_snapshot_test.go b/internal/domain/server_snapshot_test.go index 8be77f1..d4ac2d6 100644 --- a/internal/domain/server_snapshot_test.go +++ b/internal/domain/server_snapshot_test.go @@ -852,3 +852,50 @@ func TestNewListServerSnapshotsResult(t *testing.T) { assert.True(t, res.Next().IsZero()) }) } + +func TestNewListServerSnapshotsWithRelationsResult(t *testing.T) { + t.Parallel() + + snapshots := domain.ServerSnapshotsWithRelations{ + domaintest.NewServerSnapshotWithRelations(t), + domaintest.NewServerSnapshotWithRelations(t), + domaintest.NewServerSnapshotWithRelations(t), + } + next := domaintest.NewServerSnapshotWithRelations(t) + + t.Run("OK: with next", func(t *testing.T) { + t.Parallel() + + res, err := domain.NewListServerSnapshotsWithRelationsResult(snapshots, next) + require.NoError(t, err) + assert.Equal(t, snapshots, res.ServerSnapshots()) + assert.Equal(t, snapshots[0].ServerSnapshot().ID(), res.Self().ID()) + assert.Equal(t, snapshots[0].ServerSnapshot().ServerKey(), res.Self().ServerKey()) + assert.Equal(t, snapshots[0].ServerSnapshot().Date(), res.Self().Date()) + assert.Equal(t, next.ServerSnapshot().ID(), res.Next().ID()) + assert.Equal(t, next.ServerSnapshot().ServerKey(), res.Next().ServerKey()) + assert.Equal(t, next.ServerSnapshot().Date(), res.Next().Date()) + }) + + t.Run("OK: without next", func(t *testing.T) { + t.Parallel() + + res, err := domain.NewListServerSnapshotsWithRelationsResult(snapshots, domain.ServerSnapshotWithRelations{}) + require.NoError(t, err) + assert.Equal(t, snapshots, res.ServerSnapshots()) + assert.Equal(t, snapshots[0].ServerSnapshot().ID(), res.Self().ID()) + assert.Equal(t, snapshots[0].ServerSnapshot().ServerKey(), res.Self().ServerKey()) + assert.Equal(t, snapshots[0].ServerSnapshot().Date(), res.Self().Date()) + assert.True(t, res.Next().IsZero()) + }) + + t.Run("OK: 0 snapshots", func(t *testing.T) { + t.Parallel() + + res, err := domain.NewListServerSnapshotsWithRelationsResult(nil, domain.ServerSnapshotWithRelations{}) + require.NoError(t, err) + assert.Zero(t, res.ServerSnapshots()) + assert.True(t, res.Self().IsZero()) + assert.True(t, res.Next().IsZero()) + }) +} diff --git a/internal/port/handler_http_api.go b/internal/port/handler_http_api.go index 955e4cd..536315d 100644 --- a/internal/port/handler_http_api.go +++ b/internal/port/handler_http_api.go @@ -20,6 +20,7 @@ type apiHTTPHandler struct { villageSvc *app.VillageService ennoblementSvc *app.EnnoblementService tribeChangeSvc *app.TribeChangeService + serverSnapshotSvc *app.ServerSnapshotService tribeSnapshotSvc *app.TribeSnapshotService playerSnapshotSvc *app.PlayerSnapshotService errorRenderer apiErrorRenderer @@ -40,6 +41,7 @@ func NewAPIHTTPHandler( villageSvc *app.VillageService, ennoblementSvc *app.EnnoblementService, tribeChangeSvc *app.TribeChangeService, + serverSnapshotSvc *app.ServerSnapshotService, tribeSnapshotSvc *app.TribeSnapshotService, playerSnapshotSvc *app.PlayerSnapshotService, opts ...APIHTTPHandlerOption, @@ -54,6 +56,7 @@ func NewAPIHTTPHandler( villageSvc: villageSvc, ennoblementSvc: ennoblementSvc, tribeChangeSvc: tribeChangeSvc, + serverSnapshotSvc: serverSnapshotSvc, tribeSnapshotSvc: tribeSnapshotSvc, playerSnapshotSvc: playerSnapshotSvc, openAPISchema: sync.OnceValues(func() (*openapi3.T, error) { diff --git a/internal/port/handler_http_api_server_snapshot.go b/internal/port/handler_http_api_server_snapshot.go new file mode 100644 index 0000000..be80679 --- /dev/null +++ b/internal/port/handler_http_api_server_snapshot.go @@ -0,0 +1,96 @@ +package port + +import ( + "net/http" + "strconv" + + "gitea.dwysokinski.me/twhelp/core/internal/domain" + "gitea.dwysokinski.me/twhelp/core/internal/port/internal/apimodel" +) + +const apiServerSnapshotSortMaxLength = 1 + +var apiServerSnapshotSortAllowedValues = []domain.ServerSnapshotSort{ + domain.ServerSnapshotSortDateASC, + domain.ServerSnapshotSortDateDESC, +} + +//nolint:gocyclo +func (h *apiHTTPHandler) ListServerServerSnapshots( + w http.ResponseWriter, + r *http.Request, + _ apimodel.VersionCodePathParam, + serverKey apimodel.ServerKeyPathParam, + params apimodel.ListServerServerSnapshotsParams, +) { + domainParams := domain.NewListServerSnapshotsParams() + + if err := domainParams.SetSort([]domain.ServerSnapshotSort{domain.ServerSnapshotSortIDASC}); err != nil { + h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err) + return + } + + if err := domainParams.SetServerKeys([]string{serverKey}); err != nil { + h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err) + return + } + + if params.Sort != nil { + if err := domainParams.PrependSortString( + *params.Sort, + apiServerSnapshotSortAllowedValues, + apiServerSnapshotSortMaxLength, + ); err != nil { + h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err) + return + } + } else { + if err := domainParams.PrependSort([]domain.ServerSnapshotSort{domain.ServerSnapshotSortDateASC}); err != nil { + h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err) + return + } + } + + if params.Limit != nil { + if err := domainParams.SetLimit(*params.Limit); err != nil { + h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err) + return + } + } + + if params.Cursor != nil { + if err := domainParams.SetEncodedCursor(*params.Cursor); err != nil { + h.errorRenderer.withErrorPathFormatter(formatListServerSnapshotsErrorPath).render(w, r, err) + return + } + } + + res, err := h.serverSnapshotSvc.ListWithRelations(r.Context(), domainParams) + if err != nil { + h.errorRenderer.render(w, r, err) + return + } + + renderJSON(w, r, http.StatusOK, apimodel.NewListServerSnapshotsResponse(res)) +} + +func formatListServerSnapshotsErrorPath(segments []domain.ErrorPathSegment) []string { + if segments[0].Model != "ListServerSnapshotsParams" { + return nil + } + + switch segments[0].Field { + case "cursor": + return []string{"$query", "cursor"} + case "limit": + return []string{"$query", "limit"} + case "sort": + path := []string{"$query", "sort"} + if segments[0].Index >= 0 { + path = append(path, strconv.Itoa(segments[0].Index)) + } + return path + default: + return nil + } +} diff --git a/internal/port/handler_http_api_server_snapshot_test.go b/internal/port/handler_http_api_server_snapshot_test.go new file mode 100644 index 0000000..a369901 --- /dev/null +++ b/internal/port/handler_http_api_server_snapshot_test.go @@ -0,0 +1,542 @@ +package port_test + +import ( + "cmp" + "fmt" + "net/http" + "net/http/httptest" + "slices" + "strconv" + "strings" + "testing" + + "gitea.dwysokinski.me/twhelp/core/internal/domain" + "gitea.dwysokinski.me/twhelp/core/internal/domain/domaintest" + "gitea.dwysokinski.me/twhelp/core/internal/port/internal/apimodel" + "github.com/brianvoe/gofakeit/v7" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const endpointListServerServerSnapshots = "/v2/versions/%s/servers/%s/snapshots" + +func TestListServerServerSnapshots(t *testing.T) { + t.Parallel() + + handler := newAPIHTTPHandler(t) + sss := getAllServerSnapshots(t, handler) + var server serverWithVersion + + sssGroupedByServerKey := make(map[string][]serverSnapshotWithServer) + for _, ss := range sss { + key := ss.Server.Key + sssGroupedByServerKey[key] = append(sssGroupedByServerKey[key], ss) + } + currentMax := -1 + for _, grouped := range sssGroupedByServerKey { + if l := len(grouped); l > currentMax && l > 0 { + currentMax = l + server = grouped[0].Server + } + } + + tests := []struct { + name string + reqModifier func(t *testing.T, req *http.Request) + assertResp func(t *testing.T, req *http.Request, resp *http.Response) + }{ + { + name: "OK: without params", + assertResp: func(t *testing.T, _ *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body) + assert.Zero(t, body.Cursor.Next) + assert.NotZero(t, body.Cursor.Self) + assert.NotZero(t, body.Data) + assert.True(t, slices.IsSortedFunc(body.Data, func(a, b apimodel.ServerSnapshot) int { + return cmp.Or( + a.Date.Compare(b.Date.Time), + cmp.Compare(a.Id, b.Id), + ) + })) + }, + }, + { + name: "OK: limit=1", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("limit", "1") + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body) + assert.NotZero(t, body.Cursor.Next) + assert.NotZero(t, body.Cursor.Self) + assert.NotZero(t, body.Data) + limit, err := strconv.Atoi(req.URL.Query().Get("limit")) + require.NoError(t, err) + assert.Len(t, body.Data, limit) + }, + }, + { + name: "OK: limit=1 cursor", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + + q := req.URL.Query() + q.Set("limit", "1") + req.URL.RawQuery = q.Encode() + + resp := doCustomRequest(handler, req.Clone(req.Context())) + defer resp.Body.Close() + body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body) + require.NotEmpty(t, body.Cursor.Next) + + q.Set("cursor", body.Cursor.Next) + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body) + assert.Equal(t, req.URL.Query().Get("cursor"), body.Cursor.Self) + limit, err := strconv.Atoi(req.URL.Query().Get("limit")) + require.NoError(t, err) + assert.Len(t, body.Data, limit) + }, + }, + { + name: "OK: sort=[date:DESC]", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("sort", "date:DESC") + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, _ *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ListServerSnapshotsResponse](t, resp.Body) + assert.Zero(t, body.Cursor.Next) + assert.NotZero(t, body.Cursor.Self) + assert.NotZero(t, body.Data) + assert.True(t, slices.IsSortedFunc(body.Data, func(a, b apimodel.ServerSnapshot) int { + return cmp.Or( + a.Date.Compare(b.Date.Time)*-1, + cmp.Compare(a.Id, b.Id), + ) + })) + }, + }, + { + name: "ERR: limit is not an integer", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("limit", "asd") + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: "invalid-param-format", + Message: fmt.Sprintf( + "error binding string parameter: strconv.ParseInt: parsing \"%s\": invalid syntax", + req.URL.Query().Get("limit"), + ), + Path: []string{"$query", "limit"}, + }, + }, + }, body) + }, + }, + { + name: "ERR: limit < 1", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("limit", "0") + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + limit, err := strconv.Atoi(req.URL.Query().Get("limit")) + require.NoError(t, err) + domainErr := domain.MinGreaterEqualError{ + Min: 1, + Current: limit, + } + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Params: map[string]any{ + "current": float64(domainErr.Current), + "min": float64(domainErr.Min), + }, + Path: []string{"$query", "limit"}, + }, + }, + }, body) + }, + }, + { + name: fmt.Sprintf("ERR: limit > %d", domain.ServerSnapshotListMaxLimit), + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("limit", strconv.Itoa(domain.ServerSnapshotListMaxLimit+1)) + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + limit, err := strconv.Atoi(req.URL.Query().Get("limit")) + require.NoError(t, err) + domainErr := domain.MaxLessEqualError{ + Max: domain.ServerSnapshotListMaxLimit, + Current: limit, + } + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Params: map[string]any{ + "current": float64(domainErr.Current), + "max": float64(domainErr.Max), + }, + Path: []string{"$query", "limit"}, + }, + }, + }, body) + }, + }, + { + name: "ERR: len(cursor) < 1", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("cursor", "") + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + domainErr := domain.LenOutOfRangeError{ + Min: 1, + Max: 1000, + Current: len(req.URL.Query().Get("cursor")), + } + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Params: map[string]any{ + "current": float64(domainErr.Current), + "max": float64(domainErr.Max), + "min": float64(domainErr.Min), + }, + Path: []string{"$query", "cursor"}, + }, + }, + }, body) + }, + }, + { + name: "ERR: len(cursor) > 1000", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("cursor", gofakeit.LetterN(1001)) + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + domainErr := domain.LenOutOfRangeError{ + Min: 1, + Max: 1000, + Current: len(req.URL.Query().Get("cursor")), + } + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Params: map[string]any{ + "current": float64(domainErr.Current), + "max": float64(domainErr.Max), + "min": float64(domainErr.Min), + }, + Path: []string{"$query", "cursor"}, + }, + }, + }, body) + }, + }, + { + name: "ERR: invalid cursor", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("cursor", gofakeit.LetterN(100)) + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, _ *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + var domainErr domain.Error + require.ErrorAs(t, domain.ErrInvalidCursor, &domainErr) + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Path: []string{"$query", "cursor"}, + }, + }, + }, body) + }, + }, + { + name: "ERR: len(sort) > 1", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Add("sort", "date:DESC") + q.Add("sort", "date:ASC") + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + domainErr := domain.LenOutOfRangeError{ + Min: 0, + Max: 1, + Current: len(req.URL.Query()["sort"]), + } + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Params: map[string]any{ + "current": float64(domainErr.Current), + "max": float64(domainErr.Max), + "min": float64(domainErr.Min), + }, + Path: []string{"$query", "sort"}, + }, + }, + }, body) + }, + }, + { + name: "ERR: invalid sort", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Add("sort", "date:") + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + domainErr := domain.UnsupportedSortStringError{ + Sort: req.URL.Query()["sort"][0], + } + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Params: map[string]any{ + "sort": domainErr.Sort, + }, + Path: []string{"$query", "sort", "0"}, + }, + }, + }, body) + }, + }, + { + name: "ERR: version not found", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + req.URL.Path = fmt.Sprintf( + endpointListServerServerSnapshots, + randInvalidVersionCode(t, handler), + server.Key, + ) + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusNotFound, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + pathSegments := strings.Split(req.URL.Path, "/") + require.Len(t, pathSegments, 7) + domainErr := domain.VersionNotFoundError{ + VersionCode: pathSegments[3], + } + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Params: map[string]any{ + "code": domainErr.VersionCode, + }, + }, + }, + }, body) + }, + }, + { + name: "ERR: server not found", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + req.URL.Path = fmt.Sprintf( + endpointListServerServerSnapshots, + server.Version.Code, + domaintest.RandServerKey(), + ) + }, + assertResp: func(t *testing.T, req *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusNotFound, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ErrorResponse](t, resp.Body) + pathSegments := strings.Split(req.URL.Path, "/") + require.Len(t, pathSegments, 7) + domainErr := domain.ServerNotFoundError{ + Key: pathSegments[5], + } + assert.Equal(t, apimodel.ErrorResponse{ + Errors: []apimodel.Error{ + { + Code: apimodel.ErrorCode(domainErr.Code()), + Message: domainErr.Error(), + Params: map[string]any{ + "key": domainErr.Key, + }, + }, + }, + }, body) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + req := httptest.NewRequest( + http.MethodGet, + fmt.Sprintf(endpointListServerServerSnapshots, server.Version.Code, server.Key), + nil, + ) + if tt.reqModifier != nil { + tt.reqModifier(t, req) + } + + resp := doCustomRequest(handler, req) + defer resp.Body.Close() + tt.assertResp(t, req, resp) + }) + } +} + +type serverSnapshotWithServer struct { + apimodel.ServerSnapshot + Server serverWithVersion +} + +func getAllServerSnapshots(tb testing.TB, h http.Handler) []serverSnapshotWithServer { + tb.Helper() + + servers := getAllServers(tb, h) + + var sss []serverSnapshotWithServer + + for _, s := range servers { + resp := doRequest(h, http.MethodGet, fmt.Sprintf( + endpointListServerServerSnapshots, + s.Version.Code, + s.Key, + ), nil) + require.Equal(tb, http.StatusOK, resp.StatusCode) + + for _, ts := range decodeJSON[apimodel.ListServerSnapshotsResponse](tb, resp.Body).Data { + sss = append(sss, serverSnapshotWithServer{ + ServerSnapshot: ts, + Server: s, + }) + } + + _ = resp.Body.Close() + } + + require.NotZero(tb, sss) + + return sss +} diff --git a/internal/port/handler_http_api_test.go b/internal/port/handler_http_api_test.go index 49efed0..8b24f9e 100644 --- a/internal/port/handler_http_api_test.go +++ b/internal/port/handler_http_api_test.go @@ -40,6 +40,7 @@ func newAPIHTTPHandler(tb testing.TB, opts ...func(cfg *apiHTTPHandlerConfig)) h villageRepo := adapter.NewVillageBunRepository(bunDB) ennoblementRepo := adapter.NewEnnoblementBunRepository(bunDB) tribeChangeRepo := adapter.NewTribeChangeBunRepository(bunDB) + serverSnapshotRepo := adapter.NewServerSnapshotBunRepository(bunDB) tribeSnapshotRepo := adapter.NewTribeSnapshotBunRepository(bunDB) playerSnapshotRepo := adapter.NewPlayerSnapshotBunRepository(bunDB) @@ -51,6 +52,7 @@ func newAPIHTTPHandler(tb testing.TB, opts ...func(cfg *apiHTTPHandlerConfig)) h app.NewVillageService(villageRepo, nil, nil), app.NewEnnoblementService(ennoblementRepo, nil, nil), app.NewTribeChangeService(tribeChangeRepo), + app.NewServerSnapshotService(serverSnapshotRepo, nil, nil), app.NewTribeSnapshotService(tribeSnapshotRepo, nil, nil), app.NewPlayerSnapshotService(playerSnapshotRepo, nil, nil), cfg.options..., diff --git a/internal/port/internal/apimodel/server_snapshot.go b/internal/port/internal/apimodel/server_snapshot.go new file mode 100644 index 0000000..b26cd72 --- /dev/null +++ b/internal/port/internal/apimodel/server_snapshot.go @@ -0,0 +1,43 @@ +package apimodel + +import ( + "gitea.dwysokinski.me/twhelp/core/internal/domain" + oapitypes "github.com/oapi-codegen/runtime/types" +) + +func NewServerSnapshot(withRelations domain.ServerSnapshotWithRelations) ServerSnapshot { + ss := withRelations.ServerSnapshot() + return ServerSnapshot{ + Date: oapitypes.Date{Time: ss.Date()}, + Id: ss.ID(), + NumActivePlayers: ss.NumActivePlayers(), + NumActiveTribes: ss.NumActiveTribes(), + NumBarbarianVillages: ss.NumBarbarianVillages(), + NumBonusVillages: ss.NumBonusVillages(), + NumInactivePlayers: ss.NumInactivePlayers(), + NumInactiveTribes: ss.NumInactiveTribes(), + NumPlayerVillages: ss.NumPlayerVillages(), + NumPlayers: ss.NumPlayers(), + NumTribes: ss.NumTribes(), + NumVillages: ss.NumVillages(), + Server: NewServerMeta(withRelations.Server()), + } +} + +func NewListServerSnapshotsResponse(res domain.ListServerSnapshotsWithRelationsResult) ListServerSnapshotsResponse { + sss := res.ServerSnapshots() + + resp := ListServerSnapshotsResponse{ + Data: make([]ServerSnapshot, 0, len(sss)), + Cursor: Cursor{ + Next: res.Next().Encode(), + Self: res.Self().Encode(), + }, + } + + for _, ss := range sss { + resp.Data = append(resp.Data, NewServerSnapshot(ss)) + } + + return resp +} diff --git a/internal/port/testdata/api/fixture.yml b/internal/port/testdata/api/fixture.yml index 22622cf..392cc81 100644 --- a/internal/port/testdata/api/fixture.yml +++ b/internal/port/testdata/api/fixture.yml @@ -7393,6 +7393,64 @@ new_tribe_id: 2 server_key: pl169 created_at: 2021-09-10T20:01:11.000Z +- model: ServerSnapshot + rows: + - id: 10000 + server_key: de188 + num_players: 0 + num_active_players: 180 + num_inactive_players: 0 + num_tribes: 0 + num_active_tribes: 76 + num_inactive_tribes: 0 + num_villages: 16180 + num_player_villages: 15000 + num_barbarian_villages: 1180 + num_bonus_villages: 512 + date: 2024-05-01T05:15:25.154992Z + created_at: 2024-05-01T05:15:25.154994Z + - id: 10001 + server_key: de188 + num_players: 0 + num_active_players: 180 + num_inactive_players: 0 + num_tribes: 0 + num_active_tribes: 76 + num_inactive_tribes: 0 + num_villages: 16180 + num_player_villages: 15000 + num_barbarian_villages: 1180 + num_bonus_villages: 512 + date: 2024-05-02T05:15:25.154992Z + created_at: 2024-05-02T05:15:25.154994Z + - id: 20000 + server_key: it70 + num_players: 0 + num_active_players: 180 + num_inactive_players: 0 + num_tribes: 0 + num_active_tribes: 76 + num_inactive_tribes: 0 + num_villages: 16180 + num_player_villages: 15000 + num_barbarian_villages: 1180 + num_bonus_villages: 512 + date: 2024-05-03T05:15:25.154992Z + created_at: 2024-05-03T05:15:25.154994Z + - id: 20001 + server_key: it70 + num_players: 0 + num_active_players: 180 + num_inactive_players: 0 + num_tribes: 0 + num_active_tribes: 76 + num_inactive_tribes: 0 + num_villages: 16180 + num_player_villages: 15000 + num_barbarian_villages: 1180 + num_bonus_villages: 512 + date: 2024-05-04T05:15:25.154992Z + created_at: 2024-05-04T05:15:25.154994Z - model: TribeSnapshot rows: - rank_att: 1 diff --git a/internal/port/testdata/snapshotcreation/fixture.yml b/internal/port/testdata/snapshotcreation/fixture.yml index c761926..2efc384 100644 --- a/internal/port/testdata/snapshotcreation/fixture.yml +++ b/internal/port/testdata/snapshotcreation/fixture.yml @@ -5,8 +5,12 @@ url: https://pl169.plemiona.pl open: true special: false + num_players: 10000 num_active_players: 2001 + num_inactive_players: 7999 + num_tribes: 500 num_active_tribes: 214 + num_inactive_tribes: 286 num_villages: 49074 num_player_villages: 48500 num_barbarian_villages: 1574 diff --git a/k8s/base/api.yml b/k8s/base/api.yml index 378c3ab..0b5476a 100644 --- a/k8s/base/api.yml +++ b/k8s/base/api.yml @@ -39,8 +39,6 @@ spec: key: rabbitmq-connection-string - name: AUTO_MAX_PROCS value: "true" - - name: API_OPENAPI_SERVERS - value: https://twhelp.app,https://tribalwarshelp.com resources: requests: cpu: 20m diff --git a/k8s/overlays/prod/api.yml b/k8s/overlays/prod/api.yml index f19c7d0..f1a14d0 100644 --- a/k8s/overlays/prod/api.yml +++ b/k8s/overlays/prod/api.yml @@ -29,3 +29,5 @@ spec: key: rabbitmq-connection-string - name: AUTO_MAX_PROCS value: "true" + - name: API_OPENAPI_SERVERS + value: https://twhelp.app,https://tribalwarshelp.com -- 2.45.1