3 Commits

Author SHA1 Message Date
edmand46 6e7c818f75 remove UGC surface, add CI, versioning docs
CI / build (push) Successful in 1m14s
2026-08-19 17:49:03 +03:00
edmand46 ae74fb22d3 add player data operations: update, ban/unban, delete, inventory adjust, storage
Players.Update/Ban/Unban/Delete + List search; new InventoryService.Adjust
and StorageService.Get/Upsert/Delete; IdempotencyKey on wallet adjust;
models regenerated via apigen
2026-08-13 08:47:14 +03:00
edmand46 d602a95baf Add ProjectStorageService (list/get/upsert/delete) 2026-08-12 23:58:34 +03:00
27 changed files with 364 additions and 151 deletions
+26
View File
@@ -0,0 +1,26 @@
name: CI
on:
push:
branches: [master, main]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Build
run: go build ./...
- name: Vet
run: go vet ./...
+1
View File
@@ -0,0 +1 @@
.idea/
+6 -1
View File
@@ -15,6 +15,12 @@ go get hub.rudder.build/rudder/rudder-go-sdk@latest
Требуется Go 1.21+. Токен не нужен — репозиторий публичный.
## Версионирование
Потребители фиксируют версию по git-тегу
(`go get hub.rudder.build/rudder/rudder-go-sdk@vX.Y.Z`). Первый тег `v0.1.0`
будет создан при релизе; до него версионирования нет.
## Доступ
`APIKey` — это per-project Admin Key: поле «Admin Key» в настройках проекта
@@ -40,7 +46,6 @@ wallet, err := client.Wallet.Adjust(ctx, playerID, rudder.AdjustWalletParameters
Reason: "compensation",
})
metrics, err := client.Metrics.Get(ctx)
page, err := client.Ugc.List(ctx, rudder.ListUgcParameters{Status: "pending"})
```
## Ошибки
+8 -2
View File
@@ -19,7 +19,10 @@ type Client struct {
Metrics *MetricsService
Leaderboards *LeaderboardsService
Wallet *WalletService
Ugc *UgcService
Inventory *InventoryService
Storage *StorageService
ProjectStorage *ProjectStorageService
}
func New(config Config) (*Client, error) {
@@ -48,6 +51,9 @@ func New(config Config) (*Client, error) {
Metrics: &MetricsService{transport: transport},
Leaderboards: &LeaderboardsService{transport: transport},
Wallet: &WalletService{transport: transport},
Ugc: &UgcService{transport: transport},
Inventory: &InventoryService{transport: transport},
Storage: &StorageService{transport: transport},
ProjectStorage: &ProjectStorageService{transport: transport},
}, nil
}
+35
View File
@@ -0,0 +1,35 @@
package rudder
import (
"context"
"net/http"
"net/url"
"hub.rudder.build/rudder/rudder-go-sdk/models"
)
type InventoryService struct {
transport *transport
}
type AdjustInventoryParameters struct {
Slug string
Amount int64
Reason string
IdempotencyKey string
}
func (s *InventoryService) Adjust(ctx context.Context, playerID string, parameters AdjustInventoryParameters) (*models.InventoryItem, error) {
body := models.AdjustPlayerInventoryItemRequest{
Slug: parameters.Slug,
Amount: parameters.Amount,
Reason: parameters.Reason,
IdempotencyKey: parameters.IdempotencyKey,
}
var response models.InventoryItem
if err := s.transport.do(ctx, http.MethodPost, "/players/"+url.PathEscape(playerID)+"/inventory/adjust", nil, body, &response); err != nil {
return nil, err
}
return &response, nil
}
@@ -0,0 +1,10 @@
// Code generated by apigen. DO NOT EDIT.
package models
type AdjustPlayerInventoryItemRequest struct {
Amount int64 `json:"amount,omitempty"`
IdempotencyKey string `json:"idempotencyKey,omitempty"`
Reason string `json:"reason,omitempty"`
Slug string `json:"slug,omitempty"`
}
+5 -4
View File
@@ -3,8 +3,9 @@
package models
type AdjustPlayerWalletRequest struct {
Amount int64 `json:"amount,omitempty"`
CurrencyCode string `json:"currencyCode,omitempty"`
PlayerID string `json:"playerId,omitempty"`
Reason string `json:"reason,omitempty"`
Amount int64 `json:"amount,omitempty"`
CurrencyCode string `json:"currencyCode,omitempty"`
IdempotencyKey string `json:"idempotencyKey,omitempty"`
PlayerID string `json:"playerId,omitempty"`
Reason string `json:"reason,omitempty"`
}
+7
View File
@@ -0,0 +1,7 @@
// Code generated by apigen. DO NOT EDIT.
package models
type AdminGetProjectStorageResponse struct {
Item ProjectStorageItem `json:"item,omitempty"`
}
+15 -11
View File
@@ -5,15 +5,19 @@ package models
import "encoding/json"
type AdminPlayer struct {
CreatedAt string `json:"createdAt,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
ID string `json:"id,omitempty"`
Language string `json:"language,omitempty"`
LastSeen string `json:"lastSeen,omitempty"`
Nickname string `json:"nickname,omitempty"`
PayerFlag bool `json:"payerFlag,omitempty"`
ProjectID string `json:"projectId,omitempty"`
Region string `json:"region,omitempty"`
StableID string `json:"stableId,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
BanReason string `json:"banReason,omitempty"`
BannedAt string `json:"bannedAt,omitempty"`
BannedUntil string `json:"bannedUntil,omitempty"`
CreatedAt string `json:"createdAt,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
DeletedAt string `json:"deletedAt,omitempty"`
ID string `json:"id,omitempty"`
Language string `json:"language,omitempty"`
LastSeen string `json:"lastSeen,omitempty"`
Nickname string `json:"nickname,omitempty"`
PayerFlag bool `json:"payerFlag,omitempty"`
ProjectID string `json:"projectId,omitempty"`
Region string `json:"region,omitempty"`
StableID string `json:"stableId,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
}
+11
View File
@@ -0,0 +1,11 @@
// Code generated by apigen. DO NOT EDIT.
package models
type AdminProjectStorageItem struct {
Data string `json:"data,omitempty"`
ExpiresAt string `json:"expiresAt,omitempty"`
ReadPermission string `json:"readPermission,omitempty"`
Type string `json:"type,omitempty"`
WritePermission string `json:"writePermission,omitempty"`
}
+9
View File
@@ -0,0 +1,9 @@
// Code generated by apigen. DO NOT EDIT.
package models
type BanPlayerRequest struct {
BannedUntil string `json:"bannedUntil,omitempty"`
IdempotencyKey string `json:"idempotencyKey,omitempty"`
Reason string `json:"reason,omitempty"`
}
+7
View File
@@ -0,0 +1,7 @@
// Code generated by apigen. DO NOT EDIT.
package models
type DeletePlayerRequest struct {
IdempotencyKey string `json:"idempotencyKey,omitempty"`
}
+8
View File
@@ -0,0 +1,8 @@
// Code generated by apigen. DO NOT EDIT.
package models
type GetProjectStorageResponse struct {
Items []ProjectStorageItem `json:"items,omitempty"`
NextCursor string `json:"nextCursor,omitempty"`
}
-8
View File
@@ -1,8 +0,0 @@
// Code generated by apigen. DO NOT EDIT.
package models
type ListUgcResponse struct {
Items []UgcSubmission `json:"items,omitempty"`
NextCursor string `json:"nextCursor,omitempty"`
}
+15
View File
@@ -0,0 +1,15 @@
// Code generated by apigen. DO NOT EDIT.
package models
type ProjectStorageItem struct {
Data string `json:"data,omitempty"`
ExpiresAt string `json:"expiresAt,omitempty"`
ID string `json:"id,omitempty"`
ReadPermission string `json:"readPermission,omitempty"`
Size int64 `json:"size,omitempty"`
Type string `json:"type,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
Version int64 `json:"version,omitempty"`
WritePermission string `json:"writePermission,omitempty"`
}
-9
View File
@@ -1,9 +0,0 @@
// Code generated by apigen. DO NOT EDIT.
package models
type ReviewUgcRequest struct {
ID string `json:"id,omitempty"`
ReviewedBy string `json:"reviewedBy,omitempty"`
Status string `json:"status,omitempty"`
}
-8
View File
@@ -1,8 +0,0 @@
// Code generated by apigen. DO NOT EDIT.
package models
type SubmittedBy struct {
UserID string `json:"userId,omitempty"`
Username string `json:"username,omitempty"`
}
-19
View File
@@ -1,19 +0,0 @@
// Code generated by apigen. DO NOT EDIT.
package models
import "encoding/json"
type UgcSubmission struct {
Description string `json:"description,omitempty"`
FileSize int64 `json:"fileSize,omitempty"`
FileURL string `json:"fileUrl,omitempty"`
ID string `json:"id,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty"`
Name string `json:"name,omitempty"`
ReviewedAt string `json:"reviewedAt,omitempty"`
ReviewedBy string `json:"reviewedBy,omitempty"`
Status string `json:"status,omitempty"`
SubmittedAt string `json:"submittedAt,omitempty"`
SubmittedBy SubmittedBy `json:"submittedBy,omitempty"`
}
+10
View File
@@ -0,0 +1,10 @@
// Code generated by apigen. DO NOT EDIT.
package models
import "encoding/json"
type UpdatePlayerRequest struct {
Data json.RawMessage `json:"data,omitempty"`
Nickname string `json:"nickname,omitempty"`
}
-10
View File
@@ -1,10 +0,0 @@
// Code generated by apigen. DO NOT EDIT.
package models
import "encoding/json"
type UpdateUgcMetadataRequest struct {
ID string `json:"id,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty"`
}
+8
View File
@@ -0,0 +1,8 @@
// Code generated by apigen. DO NOT EDIT.
package models
type UpsertPlayerStorageRequest struct {
Data string `json:"data,omitempty"`
ExpiresAt string `json:"expiresAt,omitempty"`
}
+7
View File
@@ -0,0 +1,7 @@
// Code generated by apigen. DO NOT EDIT.
package models
type UpsertProjectStorageRequest struct {
Items []AdminProjectStorageItem `json:"items,omitempty"`
}
+63
View File
@@ -2,9 +2,11 @@ package rudder
import (
"context"
"encoding/json"
"net/http"
"net/url"
"strconv"
"time"
"hub.rudder.build/rudder/rudder-go-sdk/models"
)
@@ -16,6 +18,7 @@ type PlayersService struct {
type ListPlayersParameters struct {
Limit int
Offset int
Search string
}
func (s *PlayersService) List(ctx context.Context, parameters ListPlayersParameters) (*models.ListPlayersResponse, error) {
@@ -26,6 +29,9 @@ func (s *PlayersService) List(ctx context.Context, parameters ListPlayersParamet
if parameters.Offset > 0 {
query.Set("offset", strconv.Itoa(parameters.Offset))
}
if parameters.Search != "" {
query.Set("search", parameters.Search)
}
var response models.ListPlayersResponse
if err := s.transport.do(ctx, http.MethodGet, "/players/list", query, nil, &response); err != nil {
@@ -41,3 +47,60 @@ func (s *PlayersService) Get(ctx context.Context, playerID string) (*models.Admi
}
return &response, nil
}
type UpdatePlayerParameters struct {
Nickname *string
Data json.RawMessage
}
func (s *PlayersService) Update(ctx context.Context, playerID string, parameters UpdatePlayerParameters) (*models.AdminPlayer, error) {
body := models.UpdatePlayerRequest{
Data: parameters.Data,
}
if parameters.Nickname != nil {
body.Nickname = *parameters.Nickname
}
var response models.AdminPlayer
if err := s.transport.do(ctx, http.MethodPatch, "/players/"+url.PathEscape(playerID), nil, body, &response); err != nil {
return nil, err
}
return &response, nil
}
type BanPlayerParameters struct {
Reason string
Until *time.Time
IdempotencyKey string
}
func (s *PlayersService) Ban(ctx context.Context, playerID string, parameters BanPlayerParameters) (*models.AdminPlayer, error) {
body := models.BanPlayerRequest{
Reason: parameters.Reason,
IdempotencyKey: parameters.IdempotencyKey,
}
if parameters.Until != nil {
body.BannedUntil = parameters.Until.Format(time.RFC3339)
}
var response models.AdminPlayer
if err := s.transport.do(ctx, http.MethodPost, "/players/"+url.PathEscape(playerID)+"/ban", nil, body, &response); err != nil {
return nil, err
}
return &response, nil
}
func (s *PlayersService) Unban(ctx context.Context, playerID string) (*models.AdminPlayer, error) {
var response models.AdminPlayer
if err := s.transport.do(ctx, http.MethodPost, "/players/"+url.PathEscape(playerID)+"/unban", nil, nil, &response); err != nil {
return nil, err
}
return &response, nil
}
func (s *PlayersService) Delete(ctx context.Context, playerID string, idempotencyKey string) error {
body := models.DeletePlayerRequest{
IdempotencyKey: idempotencyKey,
}
return s.transport.do(ctx, http.MethodDelete, "/players/"+url.PathEscape(playerID), nil, body, nil)
}
+58
View File
@@ -0,0 +1,58 @@
package rudder
import (
"context"
"net/http"
"net/url"
"strconv"
"hub.rudder.build/rudder/rudder-go-sdk/models"
)
type ProjectStorageService struct {
transport *transport
}
type ListProjectStorageParameters struct {
Limit int
Cursor string
}
func (s *ProjectStorageService) List(ctx context.Context, parameters ListProjectStorageParameters) (*models.GetProjectStorageResponse, error) {
query := url.Values{}
if parameters.Limit > 0 {
query.Set("limit", strconv.Itoa(parameters.Limit))
}
if parameters.Cursor != "" {
query.Set("cursor", parameters.Cursor)
}
var response models.GetProjectStorageResponse
if err := s.transport.do(ctx, http.MethodGet, "/project-storage", query, nil, &response); err != nil {
return nil, err
}
return &response, nil
}
func (s *ProjectStorageService) Get(ctx context.Context, itemType string) (*models.ProjectStorageItem, error) {
var response models.AdminGetProjectStorageResponse
if err := s.transport.do(ctx, http.MethodGet, "/project-storage/"+url.PathEscape(itemType), nil, nil, &response); err != nil {
return nil, err
}
return &response.Item, nil
}
type UpsertProjectStorageParameters struct {
Items []models.AdminProjectStorageItem
}
func (s *ProjectStorageService) Upsert(ctx context.Context, parameters UpsertProjectStorageParameters) error {
body := models.UpsertProjectStorageRequest{
Items: parameters.Items,
}
return s.transport.do(ctx, http.MethodPut, "/project-storage", nil, body, nil)
}
func (s *ProjectStorageService) Delete(ctx context.Context, itemType string) error {
return s.transport.do(ctx, http.MethodDelete, "/project-storage/"+url.PathEscape(itemType), nil, nil, nil)
}
+46
View File
@@ -0,0 +1,46 @@
package rudder
import (
"context"
"net/http"
"net/url"
"time"
"hub.rudder.build/rudder/rudder-go-sdk/models"
)
type StorageService struct {
transport *transport
}
func (s *StorageService) Get(ctx context.Context, playerID string, storageType string) (*models.PlayerStorage, error) {
var response models.PlayerStorage
if err := s.transport.do(ctx, http.MethodGet, "/players/"+url.PathEscape(playerID)+"/storage/"+url.PathEscape(storageType), nil, nil, &response); err != nil {
return nil, err
}
return &response, nil
}
type UpsertStorageParameters struct {
Data string
ExpiresAt *time.Time
}
func (s *StorageService) Upsert(ctx context.Context, playerID string, storageType string, parameters UpsertStorageParameters) (*models.PlayerStorage, error) {
body := models.UpsertPlayerStorageRequest{
Data: parameters.Data,
}
if parameters.ExpiresAt != nil {
body.ExpiresAt = parameters.ExpiresAt.Format(time.RFC3339)
}
var response models.PlayerStorage
if err := s.transport.do(ctx, http.MethodPut, "/players/"+url.PathEscape(playerID)+"/storage/"+url.PathEscape(storageType), nil, body, &response); err != nil {
return nil, err
}
return &response, nil
}
func (s *StorageService) Delete(ctx context.Context, playerID string, storageType string) error {
return s.transport.do(ctx, http.MethodDelete, "/players/"+url.PathEscape(playerID)+"/storage/"+url.PathEscape(storageType), nil, nil, nil)
}
-72
View File
@@ -1,72 +0,0 @@
package rudder
import (
"context"
"encoding/json"
"net/http"
"net/url"
"strconv"
"hub.rudder.build/rudder/rudder-go-sdk/models"
)
type UgcService struct {
transport *transport
}
type ListUgcParameters struct {
Status string
Limit int
Cursor string
}
func (s *UgcService) List(ctx context.Context, parameters ListUgcParameters) (*models.ListUgcResponse, error) {
query := url.Values{}
if parameters.Status != "" {
query.Set("status", parameters.Status)
}
if parameters.Limit > 0 {
query.Set("limit", strconv.Itoa(parameters.Limit))
}
if parameters.Cursor != "" {
query.Set("cursor", parameters.Cursor)
}
var response models.ListUgcResponse
if err := s.transport.do(ctx, http.MethodGet, "/ugc", query, nil, &response); err != nil {
return nil, err
}
return &response, nil
}
func (s *UgcService) UpdateMetadata(ctx context.Context, id string, metadata json.RawMessage) (*models.UgcSubmission, error) {
body := models.UpdateUgcMetadataRequest{
ID: id,
Metadata: metadata,
}
var response models.UgcSubmission
if err := s.transport.do(ctx, http.MethodPatch, "/ugc/"+url.PathEscape(id)+"/metadata", nil, body, &response); err != nil {
return nil, err
}
return &response, nil
}
type ReviewUgcParameters struct {
Status string
ReviewedBy string
}
func (s *UgcService) Review(ctx context.Context, id string, parameters ReviewUgcParameters) (*models.UgcSubmission, error) {
body := models.ReviewUgcRequest{
ID: id,
Status: parameters.Status,
ReviewedBy: parameters.ReviewedBy,
}
var response models.UgcSubmission
if err := s.transport.do(ctx, http.MethodPost, "/ugc/"+url.PathEscape(id)+"/review", nil, body, &response); err != nil {
return nil, err
}
return &response, nil
}
+9 -7
View File
@@ -13,17 +13,19 @@ type WalletService struct {
}
type AdjustWalletParameters struct {
CurrencyCode string
Amount int64
Reason string
CurrencyCode string
Amount int64
Reason string
IdempotencyKey string
}
func (s *WalletService) Adjust(ctx context.Context, playerID string, parameters AdjustWalletParameters) (*models.WalletDetails, error) {
body := models.AdjustPlayerWalletRequest{
PlayerID: playerID,
CurrencyCode: parameters.CurrencyCode,
Amount: parameters.Amount,
Reason: parameters.Reason,
PlayerID: playerID,
CurrencyCode: parameters.CurrencyCode,
Amount: parameters.Amount,
Reason: parameters.Reason,
IdempotencyKey: parameters.IdempotencyKey,
}
var response models.WalletDetails