Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a217f255b8 | |||
| 9b41c844f3 | |||
| e8cec78cda | |||
| 304d6c12c1 |
@@ -0,0 +1,32 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
Breaking change — major bump, required by the backend environments release.
|
||||||
|
The module path gains the Go major-version suffix: import
|
||||||
|
`hub.rudder.build/rudder/rudder-go-sdk/v2` and pull it with
|
||||||
|
`go get hub.rudder.build/rudder/rudder-go-sdk/v2@latest`. Nothing else about
|
||||||
|
the import changes: the package is still `rudder`.
|
||||||
|
|
||||||
|
Admin keys are now per environment. A project has exactly two environments,
|
||||||
|
`staging` and `prod`, and each has its own Admin Key, so the key you configure
|
||||||
|
decides which environment every call reads and writes. There is no environment
|
||||||
|
option on the client or on any method, and there is nothing to migrate in code
|
||||||
|
— but the key itself changed: the old project Admin Key became the `prod` key
|
||||||
|
and a separate `staging` key was generated, both visible under project
|
||||||
|
Settings. Players are per environment too, so a player id from one environment
|
||||||
|
does not resolve in the other, and the same holds for wallets, inventory,
|
||||||
|
storage, counters, quests, runs, battle pass, purchases and leaderboard
|
||||||
|
entries.
|
||||||
|
|
||||||
|
Quests are addressed by slug, because ids differ between staging and prod
|
||||||
|
while slugs are stable. `PlayerQuestParameters.QuestID` is now
|
||||||
|
`PlayerQuestParameters.QuestSlug`, `PlayerQuestRef` carries `QuestSlug`
|
||||||
|
instead of `QuestID`, `PlayerQuest` reports `QuestSlug`, and `Quest`
|
||||||
|
(the catalog definition) gained `Slug` alongside its `ID`.
|
||||||
|
`Players.VerifyAuth` additionally returns `Environment`, which always matches
|
||||||
|
the admin key's environment.
|
||||||
|
|
||||||
|
Environment create, delete and merge, and project-level admin key
|
||||||
|
regeneration, were removed from the platform. This SDK never exposed them, so
|
||||||
|
nothing is gone from its surface.
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
go env -w GOPRIVATE=hub.rudder.build/*
|
go env -w GOPRIVATE=hub.rudder.build/*
|
||||||
go get hub.rudder.build/rudder/rudder-go-sdk@latest
|
go get hub.rudder.build/rudder/rudder-go-sdk/v2@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
Требуется Go 1.21+. Токен не нужен — репозиторий публичный.
|
Требуется Go 1.21+. Токен не нужен — репозиторий публичный.
|
||||||
@@ -18,15 +18,18 @@ go get hub.rudder.build/rudder/rudder-go-sdk@latest
|
|||||||
## Версионирование
|
## Версионирование
|
||||||
|
|
||||||
Потребители фиксируют версию по git-тегу
|
Потребители фиксируют версию по git-тегу
|
||||||
(`go get hub.rudder.build/rudder/rudder-go-sdk@vX.Y.Z`). Первый тег `v0.1.0`
|
(`go get hub.rudder.build/rudder/rudder-go-sdk/v2@vX.Y.Z`). Текущая мажорная
|
||||||
будет создан при релизе; до него версионирования нет.
|
версия — `v2`, поэтому путь модуля содержит суффикс `/v2`, а импорты идут
|
||||||
|
через `hub.rudder.build/rudder/rudder-go-sdk/v2`.
|
||||||
|
|
||||||
## Доступ
|
## Доступ
|
||||||
|
|
||||||
`AdminKey` — это per-project Admin Key: поле «Admin Key» в настройках проекта
|
`AdminKey` — это Admin Key окружения: у проекта их два, для `staging` и для
|
||||||
в дашборде (app.rudder.build → проект → Settings). Ключ сам определяет
|
`prod` (app.rudder.build → проект → Settings → Environments). Ключ сам
|
||||||
проект, отдельный project ID не нужен. Храните его только на сервере —
|
определяет и проект, и окружение, поэтому ни project ID, ни параметр
|
||||||
никогда не вшивайте в игровые клиенты.
|
окружения передавать не нужно; игроки и контент разных окружений полностью
|
||||||
|
изолированы. Храните ключ только на сервере — никогда не вшивайте в игровые
|
||||||
|
клиенты.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -76,7 +79,7 @@ updated, err := player.Storage().Upsert(ctx, rudder.UpsertPlayerStorageParameter
|
|||||||
err = player.Storage().Delete(ctx, rudder.DeletePlayerStorageParameters{Type: "settings"})
|
err = player.Storage().Delete(ctx, rudder.DeletePlayerStorageParameters{Type: "settings"})
|
||||||
|
|
||||||
quests, err := player.Quests().List(ctx)
|
quests, err := player.Quests().List(ctx)
|
||||||
err = player.Quests().Reset(ctx, rudder.PlayerQuestParameters{QuestID: "daily-1"})
|
err = player.Quests().Reset(ctx, rudder.PlayerQuestParameters{QuestSlug: "daily-1"})
|
||||||
// ForceComplete и ForceClaim — аналогично
|
// ForceComplete и ForceClaim — аналогично
|
||||||
|
|
||||||
updatedPlayer, err := player.Update(ctx, rudder.UpdatePlayerParameters{Nickname: "Neo"})
|
updatedPlayer, err := player.Update(ctx, rudder.UpdatePlayerParameters{Nickname: "Neo"})
|
||||||
@@ -145,12 +148,12 @@ err = client.Players.Delete(ctx, rudder.DeletePlayersParameters{
|
|||||||
## Верификация авторизации
|
## Верификация авторизации
|
||||||
|
|
||||||
`Players.VerifyAuth` — одиночный (не batch) вызов `POST /players/auth/verify`:
|
`Players.VerifyAuth` — одиночный (не batch) вызов `POST /players/auth/verify`:
|
||||||
проверяет access token игрока и возвращает `playerId`, `projectId` и `status`
|
проверяет access token игрока и возвращает `playerId`, `projectId`,
|
||||||
(`active`/`banned`/`deleted`).
|
`environment` (`staging`/`prod`) и `status` (`active`/`banned`/`deleted`).
|
||||||
|
|
||||||
```go
|
```go
|
||||||
verification, err := client.Players.VerifyAuth(ctx, accessToken)
|
verification, err := client.Players.VerifyAuth(ctx, accessToken)
|
||||||
// verification.PlayerID, verification.ProjectID, verification.Status
|
// verification.PlayerID, verification.ProjectID, verification.Environment, verification.Status
|
||||||
```
|
```
|
||||||
|
|
||||||
`VerifyWebhookSignature` — пакетный хелпер для приёма webhook'ов с подписью
|
`VerifyWebhookSignature` — пакетный хелпер для приёма webhook'ов с подписью
|
||||||
@@ -164,7 +167,7 @@ if !rudder.VerifyWebhookSignature(secret, body, r.Header.Get("X-Signature")) {
|
|||||||
|
|
||||||
err = client.Quests.Reset(ctx, rudder.PlayerQuestsParameters{
|
err = client.Quests.Reset(ctx, rudder.PlayerQuestsParameters{
|
||||||
Items: []rudder.PlayerQuestRef{
|
Items: []rudder.PlayerQuestRef{
|
||||||
{PlayerID: "p1", QuestID: "daily-1"},
|
{PlayerID: "p1", QuestSlug: "daily-1"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// ForceComplete и ForceClaim — аналогично
|
// ForceComplete и ForceClaim — аналогично
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module hub.rudder.build/rudder/rudder-go-sdk
|
module hub.rudder.build/rudder/rudder-go-sdk/v2
|
||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hub.rudder.build/rudder/rudder-go-sdk/models"
|
"hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InventoryService struct {
|
type InventoryService struct {
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hub.rudder.build/rudder/rudder-go-sdk/models"
|
"hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LeaderboardHandle struct {
|
type LeaderboardHandle struct {
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hub.rudder.build/rudder/rudder-go-sdk/models"
|
"hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LeaderboardsService struct {
|
type LeaderboardsService struct {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package models
|
|||||||
import "encoding/json"
|
import "encoding/json"
|
||||||
|
|
||||||
type AdminPlayer struct {
|
type AdminPlayer struct {
|
||||||
|
AvatarURL string `json:"avatarUrl,omitempty"`
|
||||||
BanReason string `json:"banReason,omitempty"`
|
BanReason string `json:"banReason,omitempty"`
|
||||||
BannedAt string `json:"bannedAt,omitempty"`
|
BannedAt string `json:"bannedAt,omitempty"`
|
||||||
BannedUntil string `json:"bannedUntil,omitempty"`
|
BannedUntil string `json:"bannedUntil,omitempty"`
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type AdminPlayerDetails struct {
|
type AdminPlayerDetails struct {
|
||||||
Inventory []InventoryItem `json:"inventory,omitempty"`
|
Identities []PlayerIdentity `json:"identities,omitempty"`
|
||||||
Player AdminPlayer `json:"player,omitempty"`
|
Inventory []InventoryItem `json:"inventory,omitempty"`
|
||||||
Storages []PlayerStorage `json:"storages,omitempty"`
|
Player AdminPlayer `json:"player,omitempty"`
|
||||||
WalletTransactions []WalletAudit `json:"walletTransactions,omitempty"`
|
Storages []PlayerStorage `json:"storages,omitempty"`
|
||||||
Wallets []WalletDetails `json:"wallets,omitempty"`
|
WalletTransactions []WalletAudit `json:"walletTransactions,omitempty"`
|
||||||
|
Wallets []WalletDetails `json:"wallets,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ package models
|
|||||||
type AdminPlayerQuest struct {
|
type AdminPlayerQuest struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Objectives []AdminPlayerQuestObjective `json:"objectives,omitempty"`
|
Objectives []AdminPlayerQuestObjective `json:"objectives,omitempty"`
|
||||||
QuestID string `json:"questId,omitempty"`
|
QuestSlug string `json:"questSlug,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type BatchPlayerQuestsItem struct {
|
type BatchPlayerQuestsItem struct {
|
||||||
PlayerID string `json:"playerId,omitempty"`
|
PlayerID string `json:"playerId,omitempty"`
|
||||||
QuestID string `json:"questId,omitempty"`
|
QuestSlug string `json:"questSlug,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Code generated by apigen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
type EnvironmentName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
EnvironmentNameStaging EnvironmentName = "staging"
|
||||||
|
EnvironmentNameProd EnvironmentName = "prod"
|
||||||
|
)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Code generated by apigen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
type PlayerIdentity struct {
|
||||||
|
CreatedAt string `json:"createdAt,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Provider string `json:"provider,omitempty"`
|
||||||
|
Subject string `json:"subject,omitempty"`
|
||||||
|
}
|
||||||
@@ -8,5 +8,6 @@ type QuestDefinition struct {
|
|||||||
NextQuestID string `json:"nextQuestId,omitempty"`
|
NextQuestID string `json:"nextQuestId,omitempty"`
|
||||||
Objectives []QuestObjective `json:"objectives,omitempty"`
|
Objectives []QuestObjective `json:"objectives,omitempty"`
|
||||||
Rewards []QuestRewardDefinition `json:"rewards,omitempty"`
|
Rewards []QuestRewardDefinition `json:"rewards,omitempty"`
|
||||||
|
Slug string `json:"slug,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type VerifyPlayerAuthResponse struct {
|
type VerifyPlayerAuthResponse struct {
|
||||||
PlayerID string `json:"playerId,omitempty"`
|
Environment EnvironmentName `json:"environment,omitempty"`
|
||||||
ProjectID string `json:"projectId,omitempty"`
|
PlayerID string `json:"playerId,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
ProjectID string `json:"projectId,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"hub.rudder.build/rudder/rudder-go-sdk/models"
|
"hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlayerQuestsHandle struct {
|
type PlayerQuestsHandle struct {
|
||||||
@@ -24,26 +24,26 @@ func (h *PlayerQuestsHandle) List(ctx context.Context) ([]PlayerQuest, error) {
|
|||||||
|
|
||||||
type PlayerQuestParameters struct {
|
type PlayerQuestParameters struct {
|
||||||
IdempotencyKey string
|
IdempotencyKey string
|
||||||
QuestID string
|
QuestSlug string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *PlayerQuestsHandle) Reset(ctx context.Context, parameters PlayerQuestParameters) error {
|
func (h *PlayerQuestsHandle) Reset(ctx context.Context, parameters PlayerQuestParameters) error {
|
||||||
return h.quests.Reset(ctx, PlayerQuestsParameters{
|
return h.quests.Reset(ctx, PlayerQuestsParameters{
|
||||||
IdempotencyKey: parameters.IdempotencyKey,
|
IdempotencyKey: parameters.IdempotencyKey,
|
||||||
Items: []PlayerQuestRef{{PlayerID: h.playerID, QuestID: parameters.QuestID}},
|
Items: []PlayerQuestRef{{PlayerID: h.playerID, QuestSlug: parameters.QuestSlug}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *PlayerQuestsHandle) ForceComplete(ctx context.Context, parameters PlayerQuestParameters) error {
|
func (h *PlayerQuestsHandle) ForceComplete(ctx context.Context, parameters PlayerQuestParameters) error {
|
||||||
return h.quests.ForceComplete(ctx, PlayerQuestsParameters{
|
return h.quests.ForceComplete(ctx, PlayerQuestsParameters{
|
||||||
IdempotencyKey: parameters.IdempotencyKey,
|
IdempotencyKey: parameters.IdempotencyKey,
|
||||||
Items: []PlayerQuestRef{{PlayerID: h.playerID, QuestID: parameters.QuestID}},
|
Items: []PlayerQuestRef{{PlayerID: h.playerID, QuestSlug: parameters.QuestSlug}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *PlayerQuestsHandle) ForceClaim(ctx context.Context, parameters PlayerQuestParameters) error {
|
func (h *PlayerQuestsHandle) ForceClaim(ctx context.Context, parameters PlayerQuestParameters) error {
|
||||||
return h.quests.ForceClaim(ctx, PlayerQuestsParameters{
|
return h.quests.ForceClaim(ctx, PlayerQuestsParameters{
|
||||||
IdempotencyKey: parameters.IdempotencyKey,
|
IdempotencyKey: parameters.IdempotencyKey,
|
||||||
Items: []PlayerQuestRef{{PlayerID: h.playerID, QuestID: parameters.QuestID}},
|
Items: []PlayerQuestRef{{PlayerID: h.playerID, QuestSlug: parameters.QuestSlug}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"hub.rudder.build/rudder/rudder-go-sdk/models"
|
"hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlayerStorageHandle struct {
|
type PlayerStorageHandle struct {
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hub.rudder.build/rudder/rudder-go-sdk/models"
|
"hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlayersService struct {
|
type PlayersService struct {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hub.rudder.build/rudder/rudder-go-sdk/models"
|
"hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type QuestsService struct {
|
type QuestsService struct {
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
---
|
||||||
|
name: rudder-go-sdk
|
||||||
|
description: Use when working with the Rudder Go server SDK (package rudder, module hub.rudder.build/rudder/rudder-go-sdk/v2) for server-side admin operations — players, wallet, inventory, storage, quests, leaderboards, webhook signature verification.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Rudder Go server SDK
|
||||||
|
|
||||||
|
Server-side admin SDK for the Rudder LiveOps platform. Covers the `/game/v1`
|
||||||
|
admin surface, authenticated with `X-API-Key`. Module:
|
||||||
|
`hub.rudder.build/rudder/rudder-go-sdk/v2`, package `rudder`. Requires Go 1.21+.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
The module is served from a self-hosted Gitea, bypassing proxy.golang.org:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go env -w GOPRIVATE=hub.rudder.build/*
|
||||||
|
go get hub.rudder.build/rudder/rudder-go-sdk/v2@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client init
|
||||||
|
|
||||||
|
`AdminKey` is the Admin Key of one environment. A project has exactly two,
|
||||||
|
`staging` and `prod` (app.rudder.build → project → Settings → Environments).
|
||||||
|
The key identifies both the project and the environment, so no project ID and
|
||||||
|
no environment argument are ever passed; players, wallets, inventory, storage,
|
||||||
|
quests and leaderboard entries of the two environments are fully isolated.
|
||||||
|
Keep it server-side only, never in game clients.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import rudder "hub.rudder.build/rudder/rudder-go-sdk/v2"
|
||||||
|
|
||||||
|
client, err := rudder.New(rudder.Config{
|
||||||
|
AdminKey: os.Getenv("RUDDER_ADMIN_KEY"),
|
||||||
|
// BaseURL defaults to https://api.rudder.build
|
||||||
|
// Timeout defaults to 30s; HTTPClient optional
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
`client` exposes services: `Players`, `Leaderboards`, `Wallet`, `Inventory`,
|
||||||
|
`Quests`, `Storage`. All methods take `context.Context` first.
|
||||||
|
|
||||||
|
Public types live in package `rudder` (aliases in `types.go`). The `models`
|
||||||
|
package is generated wire format — never import it in application code and
|
||||||
|
never edit it by hand (regenerate via `make generate-openapi` in
|
||||||
|
liveops-backend).
|
||||||
|
|
||||||
|
## Batch-first mutation model
|
||||||
|
|
||||||
|
All top-level mutating operations are batch-only. Request body is
|
||||||
|
`{idempotencyKey?, items:[...]}` with `playerId` inside each item
|
||||||
|
(leaderboard delete takes `{playerIds:[...]}`, project-storage delete
|
||||||
|
`{types:[...]}`). Rules:
|
||||||
|
|
||||||
|
- at most 100 items per batch
|
||||||
|
- all-or-nothing in one transaction — one failing item fails the whole batch
|
||||||
|
- one optional `IdempotencyKey` per batch; retrying with the same key is safe
|
||||||
|
- errors report the index and playerId of the failing item
|
||||||
|
|
||||||
|
Method naming is uniform: verb + entity (`List`/`Get`/`Submit`/`Update`/
|
||||||
|
`Upsert`/`Delete`/`Adjust`/`History`).
|
||||||
|
|
||||||
|
## Handles
|
||||||
|
|
||||||
|
`client.Player(id)` and `client.Leaderboard(slug)` return handles without any
|
||||||
|
HTTP request. Handles are sugar over the batch operations with n=1: the
|
||||||
|
handle injects `playerID`/`slug` into a single-item batch and unwraps the
|
||||||
|
first result.
|
||||||
|
|
||||||
|
```go
|
||||||
|
player := client.Player("p1")
|
||||||
|
wallet, err := player.Wallet().Adjust(ctx, rudder.AdjustPlayerWalletParameters{
|
||||||
|
IdempotencyKey: "grant-001",
|
||||||
|
CurrencyCode: "coins",
|
||||||
|
Amount: 100,
|
||||||
|
Reason: "compensation",
|
||||||
|
})
|
||||||
|
|
||||||
|
board := client.Leaderboard("weekly")
|
||||||
|
err = board.Submit(ctx, []rudder.LeaderboardScore{{PlayerID: "p1", Score: 1500}})
|
||||||
|
```
|
||||||
|
|
||||||
|
`client.Player(id)` exposes sub-handles `Wallet()`, `Inventory()`,
|
||||||
|
`Storage()`, `Quests()` plus `Update`/`Ban`/`Unban`/`Delete`.
|
||||||
|
`client.Leaderboard(slug)` exposes `List`/`Get`/`GetAround`/`Submit`/
|
||||||
|
`Update`/`Delete`.
|
||||||
|
|
||||||
|
## Top-level Storage vs PlayerHandle.Storage()
|
||||||
|
|
||||||
|
`client.Storage` is the **global (project) storage** — shared key/value items
|
||||||
|
keyed by `Type`, with permissions and versioning. Player storage is reachable
|
||||||
|
**only** through `client.Player(id).Storage()` — per-player items keyed by
|
||||||
|
`Type`. They are different backends with different item shapes; do not
|
||||||
|
confuse them.
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
All API failures return `*rudder.APIError` (`Status`, `Code`, `Message`,
|
||||||
|
`RequestID`); use `errors.As`. Client-side misconfiguration returns
|
||||||
|
`Code: "sdk/invalid-options"` (`rudder.ErrorCodeInvalidOptions`). Scenario
|
||||||
|
engine codes surfaced in `Code` include `early_completion`, `forbidden`,
|
||||||
|
`level_not_reached`, `node_not_active`, `objectives_incomplete`,
|
||||||
|
`run_expired`, `run_not_active`, `scenario_not_active`, `unknown_run`.
|
||||||
|
|
||||||
|
## Webhook verification
|
||||||
|
|
||||||
|
```go
|
||||||
|
if !rudder.VerifyWebhookSignature(secret, body, r.Header.Get("X-Signature")) {
|
||||||
|
// reject: signature is hex HMAC-SHA256 of the raw body
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reference (exact signatures per service area)
|
||||||
|
|
||||||
|
- `reference/players.md` — list/get/verify-auth, batch update/ban/unban/delete, PlayerHandle
|
||||||
|
- `reference/wallet.md` — batch adjust, history, PlayerWalletHandle
|
||||||
|
- `reference/inventory.md` — batch adjust, list, PlayerInventoryHandle
|
||||||
|
- `reference/storage.md` — project storage (top-level) and player storage (handle)
|
||||||
|
- `reference/quests.md` — quest definitions list, batch reset/force-complete/force-claim, PlayerQuestsHandle
|
||||||
|
- `reference/leaderboards.md` — definitions list, LeaderboardHandle entries
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Inventory
|
||||||
|
|
||||||
|
Source: `inventory.go`, `player_inventory.go`. Types: `InventoryItem`, `InventoryList`, `InventoryAdjustment`.
|
||||||
|
|
||||||
|
## Batch adjust
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *InventoryService) Adjust(ctx context.Context, parameters AdjustInventoryParameters) ([]InventoryItem, error)
|
||||||
|
type AdjustInventoryParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Items []InventoryAdjustment
|
||||||
|
}
|
||||||
|
// InventoryAdjustment: {PlayerID, Slug, Amount int64, Reason}
|
||||||
|
// Negative Amount removes items. Returns updated items in item order.
|
||||||
|
```
|
||||||
|
|
||||||
|
## List
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *InventoryService) List(ctx context.Context, playerID string, parameters ListInventoryParameters) (*InventoryList, error)
|
||||||
|
type ListInventoryParameters struct {
|
||||||
|
Limit int
|
||||||
|
Cursor *string
|
||||||
|
}
|
||||||
|
// InventoryList: {Items []InventoryItem, NextCursor string}
|
||||||
|
// InventoryItem: {ID, Slug, Amount int64, UpdatedAt}
|
||||||
|
```
|
||||||
|
|
||||||
|
## PlayerInventoryHandle
|
||||||
|
|
||||||
|
`client.Player(id).Inventory()`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (h *PlayerInventoryHandle) Adjust(ctx context.Context, parameters AdjustPlayerInventoryParameters) (*InventoryItem, error)
|
||||||
|
type AdjustPlayerInventoryParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Slug string
|
||||||
|
Amount int64
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PlayerInventoryHandle) List(ctx context.Context, parameters ListInventoryParameters) (*InventoryList, error)
|
||||||
|
```
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Leaderboards
|
||||||
|
|
||||||
|
Source: `leaderboards.go` (definitions + batch entry mutations), `leaderboard.go` (handle).
|
||||||
|
Types: `Leaderboard`, `LeaderboardList`, `LeaderboardScore`, `Rank`, `RankList`.
|
||||||
|
|
||||||
|
## Definitions
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *LeaderboardsService) List(ctx context.Context, parameters ListLeaderboardsParameters) (*LeaderboardList, error)
|
||||||
|
type ListLeaderboardsParameters struct {
|
||||||
|
Limit int
|
||||||
|
Cursor *string
|
||||||
|
}
|
||||||
|
// LeaderboardList: {Items []Leaderboard, NextCursor string}
|
||||||
|
// Leaderboard: {Slug, Name, Metric, ResetPeriod, SortingOrder, MaxEntries int64}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Batch entry mutations (service level)
|
||||||
|
|
||||||
|
Note: leaderboard entry mutations do NOT take an IdempotencyKey — the generated
|
||||||
|
batch request bodies carry only items/playerIds.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *LeaderboardsService) Submit(ctx context.Context, slug string, items []LeaderboardScore) error
|
||||||
|
func (s *LeaderboardsService) Update(ctx context.Context, slug string, items []LeaderboardScore) error
|
||||||
|
// LeaderboardScore: {PlayerID, Score float64}
|
||||||
|
|
||||||
|
func (s *LeaderboardsService) Delete(ctx context.Context, slug string, playerIDs []string) error
|
||||||
|
// body: {playerIds: [...]}
|
||||||
|
```
|
||||||
|
|
||||||
|
## LeaderboardHandle
|
||||||
|
|
||||||
|
`client.Leaderboard(slug)` — no request at creation.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (h *LeaderboardHandle) List(ctx context.Context, parameters ListLeaderboardEntriesParameters) (*RankList, error)
|
||||||
|
type ListLeaderboardEntriesParameters struct {
|
||||||
|
Limit int
|
||||||
|
Cursor *string
|
||||||
|
}
|
||||||
|
// RankList: {Entries []Rank, NextCursor string}
|
||||||
|
|
||||||
|
func (h *LeaderboardHandle) Get(ctx context.Context, playerID string) (*Rank, error)
|
||||||
|
|
||||||
|
func (h *LeaderboardHandle) GetAround(ctx context.Context, playerID string, limit int) ([]Rank, error)
|
||||||
|
// entries around the player's rank
|
||||||
|
|
||||||
|
func (h *LeaderboardHandle) Submit(ctx context.Context, items []LeaderboardScore) error
|
||||||
|
func (h *LeaderboardHandle) Update(ctx context.Context, items []LeaderboardScore) error
|
||||||
|
func (h *LeaderboardHandle) Delete(ctx context.Context, playerIDs []string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
`Rank`: `{PlayerID, PlayerName, Rank int64, Score float64}`.
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Players
|
||||||
|
|
||||||
|
Source: `players.go`, `player.go`. Types: `types.go` (`Player`, `PlayerDetails`, `PlayerList`, `PlayerUpdate`, `PlayerBan`, `PlayerAuthVerification`).
|
||||||
|
|
||||||
|
## Reads
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *PlayersService) List(ctx context.Context, parameters ListPlayersParameters) (*PlayerList, error)
|
||||||
|
type ListPlayersParameters struct {
|
||||||
|
Limit int // offset-based pagination
|
||||||
|
Offset int
|
||||||
|
Search string
|
||||||
|
}
|
||||||
|
// PlayerList: {Players []Player, Total int, Limit int, Offset int}
|
||||||
|
|
||||||
|
func (s *PlayersService) Get(ctx context.Context, playerID string) (*PlayerDetails, error)
|
||||||
|
// PlayerDetails: {Player Player, Wallets []WalletBalance, Inventory []InventoryItem,
|
||||||
|
// Storages []PlayerStorage, WalletTransactions []WalletTransaction}
|
||||||
|
|
||||||
|
func (s *PlayersService) VerifyAuth(ctx context.Context, accessToken string) (*PlayerAuthVerification, error)
|
||||||
|
// Single (non-batch) call: POST /players/auth/verify.
|
||||||
|
// PlayerAuthVerification: {PlayerID, ProjectID, Environment, Status} — Status is "active"/"banned"/"deleted".
|
||||||
|
// Environment ("staging"/"prod") is the environment the token was issued for; it always
|
||||||
|
// matches the admin key, since both come from the same environment.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Batch mutations
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *PlayersService) Update(ctx context.Context, parameters UpdatePlayersParameters) ([]Player, error)
|
||||||
|
type UpdatePlayersParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Items []PlayerUpdate // {PlayerID, Nickname, Data json.RawMessage}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PlayersService) Ban(ctx context.Context, parameters BanPlayersParameters) ([]Player, error)
|
||||||
|
type BanPlayersParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Items []PlayerBan // {PlayerID, Reason, BannedUntil string}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PlayersService) Unban(ctx context.Context, parameters UnbanPlayersParameters) ([]Player, error)
|
||||||
|
type UnbanPlayersParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
PlayerIDs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PlayersService) Delete(ctx context.Context, parameters DeletePlayersParameters) error
|
||||||
|
type DeletePlayersParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
PlayerIDs []string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## PlayerHandle
|
||||||
|
|
||||||
|
`client.Player(id)` — no request at creation.
|
||||||
|
|
||||||
|
```go
|
||||||
|
h.Wallet() *PlayerWalletHandle
|
||||||
|
h.Inventory() *PlayerInventoryHandle
|
||||||
|
h.Storage() *PlayerStorageHandle
|
||||||
|
h.Quests() *PlayerQuestsHandle
|
||||||
|
|
||||||
|
func (h *PlayerHandle) Update(ctx context.Context, parameters UpdatePlayerParameters) (*Player, error)
|
||||||
|
type UpdatePlayerParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Nickname string
|
||||||
|
Data json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PlayerHandle) Ban(ctx context.Context, parameters BanPlayerParameters) (*Player, error)
|
||||||
|
type BanPlayerParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Reason string
|
||||||
|
BannedUntil string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PlayerHandle) Unban(ctx context.Context, idempotencyKey string) (*Player, error)
|
||||||
|
func (h *PlayerHandle) Delete(ctx context.Context, idempotencyKey string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
## Player fields
|
||||||
|
|
||||||
|
`Player`: `ID`, `StableID`, `ProjectID`, `Nickname`, `Data json.RawMessage`,
|
||||||
|
`Language`, `Region`, `PayerFlag bool`, `BanReason`, `BannedAt`,
|
||||||
|
`BannedUntil`, `CreatedAt`, `UpdatedAt`, `LastSeen`, `DeletedAt` (all
|
||||||
|
string unless noted).
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Quests
|
||||||
|
|
||||||
|
Source: `quests.go`, `player_quests.go`. Types: `Quest`, `QuestObjective`, `QuestReward`, `QuestList`, `PlayerQuest`, `PlayerQuestObjective`, `PlayerQuestRef`.
|
||||||
|
|
||||||
|
## Quest definitions (catalog)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *QuestsService) List(ctx context.Context, parameters ListQuestsParameters) (*QuestList, error)
|
||||||
|
type ListQuestsParameters struct {
|
||||||
|
Status *string // optional: "active" or "archived"
|
||||||
|
Limit int
|
||||||
|
Cursor *string
|
||||||
|
}
|
||||||
|
// QuestList: {Items []Quest, NextCursor string}
|
||||||
|
// Quest: {ID, Slug, Name, Status, NextQuestID, Objectives []QuestObjective, Rewards []QuestReward}
|
||||||
|
// QuestObjective: {ID, Metric, Target int64}
|
||||||
|
// QuestReward: {Amount int64, Currency, ItemID}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Batch player-quest operations
|
||||||
|
|
||||||
|
All three share the same parameters and return only `error`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *QuestsService) Reset(ctx context.Context, parameters PlayerQuestsParameters) error
|
||||||
|
func (s *QuestsService) ForceComplete(ctx context.Context, parameters PlayerQuestsParameters) error
|
||||||
|
func (s *QuestsService) ForceClaim(ctx context.Context, parameters PlayerQuestsParameters) error
|
||||||
|
|
||||||
|
type PlayerQuestsParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Items []PlayerQuestRef // {PlayerID, QuestSlug}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## PlayerQuestsHandle
|
||||||
|
|
||||||
|
`client.Player(id).Quests()`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (h *PlayerQuestsHandle) List(ctx context.Context) ([]PlayerQuest, error)
|
||||||
|
// PlayerQuest: {QuestSlug, Name, Status, Objectives []PlayerQuestObjective}
|
||||||
|
// PlayerQuestObjective: {ObjectiveID, Current int64, Target int64}
|
||||||
|
|
||||||
|
func (h *PlayerQuestsHandle) Reset(ctx context.Context, parameters PlayerQuestParameters) error
|
||||||
|
func (h *PlayerQuestsHandle) ForceComplete(ctx context.Context, parameters PlayerQuestParameters) error
|
||||||
|
func (h *PlayerQuestsHandle) ForceClaim(ctx context.Context, parameters PlayerQuestParameters) error
|
||||||
|
|
||||||
|
type PlayerQuestParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
QuestSlug string
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Storage
|
||||||
|
|
||||||
|
Source: `storage.go` (project storage, top-level), `player_storage.go` (player storage, handle only).
|
||||||
|
Types: `ProjectStorage`, `ProjectStorageList`, `ProjectStorageInput`, `PlayerStorage`.
|
||||||
|
|
||||||
|
Top-level `client.Storage` is **project (global) storage**. Player storage is reachable only via `client.Player(id).Storage()`.
|
||||||
|
|
||||||
|
## Project storage (client.Storage)
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *StorageService) List(ctx context.Context, parameters ListStorageParameters) (*ProjectStorageList, error)
|
||||||
|
type ListStorageParameters struct {
|
||||||
|
Limit int
|
||||||
|
Cursor string // plain string, not *string
|
||||||
|
Search string
|
||||||
|
}
|
||||||
|
// ProjectStorageList: {Items []ProjectStorage, NextCursor string}
|
||||||
|
|
||||||
|
func (s *StorageService) Get(ctx context.Context, itemType string) (*ProjectStorage, error)
|
||||||
|
|
||||||
|
func (s *StorageService) Upsert(ctx context.Context, parameters UpsertStorageParameters) error
|
||||||
|
type UpsertStorageParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Items []ProjectStorageInput
|
||||||
|
}
|
||||||
|
// ProjectStorageInput: {Type, Data, ExpiresAt, ReadPermission, WritePermission}
|
||||||
|
|
||||||
|
func (s *StorageService) Delete(ctx context.Context, parameters DeleteStorageParameters) error
|
||||||
|
type DeleteStorageParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Types []string // batch delete by item types
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`ProjectStorage` (returned item): `{ID, Type, Data, Size int64, Version int64, ReadPermission, WritePermission, ExpiresAt, UpdatedAt}`.
|
||||||
|
|
||||||
|
## Player storage (client.Player(id).Storage())
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (h *PlayerStorageHandle) Get(ctx context.Context, storageType string) (*PlayerStorage, error)
|
||||||
|
|
||||||
|
func (h *PlayerStorageHandle) Upsert(ctx context.Context, parameters UpsertPlayerStorageParameters) (*PlayerStorage, error)
|
||||||
|
type UpsertPlayerStorageParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Type string
|
||||||
|
Data string
|
||||||
|
ExpiresAt string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PlayerStorageHandle) Delete(ctx context.Context, parameters DeletePlayerStorageParameters) error
|
||||||
|
type DeletePlayerStorageParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`PlayerStorage`: `{ID, PlayerID, Type, Data, ExpiresAt, UpdatedAt}`.
|
||||||
|
|
||||||
|
The handle's Upsert/Delete send single-item batches to `PUT`/`DELETE /players/storage` — the same batch rules (idempotency key, all-or-nothing) apply.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Wallet
|
||||||
|
|
||||||
|
Source: `wallet.go`, `player_wallet.go`. Types: `WalletBalance`, `WalletTransaction`, `WalletHistory`, `WalletAdjustment`.
|
||||||
|
|
||||||
|
## Batch adjust
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *WalletService) Adjust(ctx context.Context, parameters AdjustWalletParameters) ([]WalletBalance, error)
|
||||||
|
type AdjustWalletParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
Items []WalletAdjustment
|
||||||
|
}
|
||||||
|
// WalletAdjustment: {PlayerID, CurrencyCode, Amount int64, Reason}
|
||||||
|
// Negative Amount subtracts. Returns updated balances in item order.
|
||||||
|
```
|
||||||
|
|
||||||
|
## History
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *WalletService) History(ctx context.Context, playerID string, parameters WalletHistoryParameters) (*WalletHistory, error)
|
||||||
|
type WalletHistoryParameters struct {
|
||||||
|
Currency *string // optional filter
|
||||||
|
Limit int
|
||||||
|
Cursor *string
|
||||||
|
}
|
||||||
|
// WalletHistory: {Entries []WalletTransaction, NextCursor string}
|
||||||
|
// WalletTransaction: {ID, WalletID, PlayerID, CurrencyCode, Amount int64,
|
||||||
|
// BalanceBefore int64, BalanceAfter int64, Reason, Source,
|
||||||
|
// Metadata json.RawMessage, CreatedAt}
|
||||||
|
```
|
||||||
|
|
||||||
|
## PlayerWalletHandle
|
||||||
|
|
||||||
|
`client.Player(id).Wallet()`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (h *PlayerWalletHandle) Adjust(ctx context.Context, parameters AdjustPlayerWalletParameters) (*WalletBalance, error)
|
||||||
|
type AdjustPlayerWalletParameters struct {
|
||||||
|
IdempotencyKey string
|
||||||
|
CurrencyCode string
|
||||||
|
Amount int64
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PlayerWalletHandle) History(ctx context.Context, parameters WalletHistoryParameters) (*WalletHistory, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
## WalletBalance fields
|
||||||
|
|
||||||
|
`{ID, PlayerID, CurrencyCode, Balance int64, UpdatedAt}`.
|
||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hub.rudder.build/rudder/rudder-go-sdk/models"
|
"hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StorageService struct {
|
type StorageService struct {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package rudder
|
package rudder
|
||||||
|
|
||||||
import "hub.rudder.build/rudder/rudder-go-sdk/models"
|
import "hub.rudder.build/rudder/rudder-go-sdk/v2/models"
|
||||||
|
|
||||||
// Domain types are aliases of generated OpenAPI models so callers import
|
// Domain types are aliases of generated OpenAPI models so callers import
|
||||||
// only this package. models/ is wire format and is not part of the public API.
|
// only this package. models/ is wire format and is not part of the public API.
|
||||||
|
|||||||
Reference in New Issue
Block a user