Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
17b24f2a16 | |||
cf28f6c8df | |||
2e1b00f306 | |||
57d157df6f | |||
5624a07607 | |||
c693dd2017 | |||
30a8065a46 | |||
9ffe9a377b | |||
da774aa910 | |||
b8981ad8ec | |||
be3f38e715 | |||
546d610e4e | |||
8041f6835f |
4
api.go
4
api.go
@ -97,6 +97,10 @@ func (s *Sorare) Debug() *Sorare {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Sorare) SetApiKey(apiKey string) {
|
||||||
|
s.Client.SetApiKey(apiKey)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Sorare) SetJWTToken(token graphql.JwtToken, audience string) {
|
func (s *Sorare) SetJWTToken(token graphql.JwtToken, audience string) {
|
||||||
s.Client.SetJWTToken(token, audience)
|
s.Client.SetJWTToken(token, audience)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@ -21,7 +20,7 @@ func main() {
|
|||||||
if audience == "" {
|
if audience == "" {
|
||||||
log.Fatal("No audience provided")
|
log.Fatal("No audience provided")
|
||||||
}
|
}
|
||||||
api := sorare.New().Debug()
|
api := sorare.New()
|
||||||
api.SetJWTToken(
|
api.SetJWTToken(
|
||||||
graphql.JwtToken{
|
graphql.JwtToken{
|
||||||
Token: token,
|
Token: token,
|
||||||
@ -32,18 +31,57 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
//cu, err := api.Country.Get(ctx, graphql.SlugParams{Slug: "fr"})
|
p, err := api.Football.Player.Get(ctx, graphql.SlugParams{Slug: "joshua-kimmich"})
|
||||||
params := make(map[string][]string)
|
|
||||||
params["0c7d5f17-7c01-4427-90b8-a736a44ea632"] = []string{"joshua-kimmich", "leon-goretzka"}
|
|
||||||
params["0d641a1d-c64a-40c9-85b0-7ddeca428abf"] = []string{"joshua-kimmich", "leon-goretzka"}
|
|
||||||
cu, err := api.Football.GamesScores.Get(
|
|
||||||
ctx,
|
|
||||||
params,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
for _, game := range cu {
|
log.Printf("%+v", p)
|
||||||
log.Println(game.Game.Id.Value + " " + game.Player.Slug + " " + fmt.Sprintf("%s", game.Score))
|
|
||||||
}
|
// si, err := api.ShopItems.Get(ctx, sorare.ShopItemsParams{
|
||||||
|
// Types: []sorare.ShopItemType{sorare.ShopItemTypeLevelUp},
|
||||||
|
// UnlockedOnly: true,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// for _, item := range si {
|
||||||
|
// log.Printf(
|
||||||
|
// "%s %d %d %s %s",
|
||||||
|
// item.TypeName,
|
||||||
|
// item.ShopItemInterface.MyBalance,
|
||||||
|
// item.ShopItemInterface.Price,
|
||||||
|
// item.LevelUpShopItem.Rarity,
|
||||||
|
// time.Until(item.ShopItemInterface.MyLimitResetAt).String(),
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// cu, err := api.Users.CurrentUser.Get(ctx, graphql.EmptyParams{})
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// log.Printf("%d", cu.CoinBalance)
|
||||||
|
// cards, err := api.Users.FootballCards("gigiz22").Get(ctx, football.CardsParams{
|
||||||
|
// Rarities: []types.Rarity{types.RarityLimited},
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// sort.Slice(cards, func(i, j int) bool {
|
||||||
|
// return cards[i].XpNeededForNextGrade-cards[i].Xp > cards[j].XpNeededForNextGrade-cards[j].Xp
|
||||||
|
// })
|
||||||
|
// for _, card := range cards {
|
||||||
|
// if !card.InSeasonEligible {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// log.Printf(
|
||||||
|
// "%60s %2d %6d %6d %6d (%d/%d)",
|
||||||
|
// card.Token.Name,
|
||||||
|
// card.Grade,
|
||||||
|
// card.Xp-card.XpNeededForCurrentGrade,
|
||||||
|
// card.Xp,
|
||||||
|
// card.XpNeededForNextGrade-card.Xp,
|
||||||
|
// card.LevelUpAppliedCount,
|
||||||
|
// card.MaxLevelUpAppliedCount,
|
||||||
|
// )
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Card struct {
|
type Card struct {
|
||||||
|
Token tokens.Token `graphql:"token"`
|
||||||
Power decimal.Decimal `graphql:"power"`
|
Power decimal.Decimal `graphql:"power"`
|
||||||
PowerMalusAfterTransfer decimal.Decimal `graphql:"powerMalusAfterTransfer"`
|
PowerMalusAfterTransfer decimal.Decimal `graphql:"powerMalusAfterTransfer"`
|
||||||
RivalsPower decimal.Decimal `graphql:"rivalsPower"`
|
RivalsPower decimal.Decimal `graphql:"rivalsPower"`
|
||||||
Grade int `graphql:"grade"`
|
Grade int `graphql:"grade"`
|
||||||
GradeAfterTransfer int `graphql:"gradeAfterTransfer"`
|
GradeAfterTransfer int `graphql:"gradeAfterTransfer"`
|
||||||
Xp int `graphql:"xp"`
|
Xp int `graphql:"xp"`
|
||||||
Token tokens.Token `graphql:"token"`
|
XpAfterTransfer int `graphql:"xpAfterTransfer"`
|
||||||
|
XpNeededForNextGrade int `graphql:"xpNeededForNextGrade"`
|
||||||
|
XpNeededForCurrentGrade int `graphql:"xpNeededForCurrentGrade"`
|
||||||
|
InSeasonEligible bool `graphql:"inSeasonEligible"`
|
||||||
|
LevelUpAppliedCount int `graphql:"levelUpAppliedCount"`
|
||||||
|
MaxLevelUpAppliedCount int `graphql:"maxLevelUpAppliedCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CardsParams struct {
|
type CardsParams struct {
|
||||||
|
@ -26,7 +26,8 @@ type Football struct {
|
|||||||
Players *graphql.Query[[]Player, graphql.SlugsParams]
|
Players *graphql.Query[[]Player, graphql.SlugsParams]
|
||||||
Season *graphql.Query[Season, SeasonParams]
|
Season *graphql.Query[Season, SeasonParams]
|
||||||
|
|
||||||
So5 *So5
|
So5 *So5
|
||||||
|
Rivals *Rivals
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFootball(c *graphql.Client) *Football {
|
func NewFootball(c *graphql.Client) *Football {
|
||||||
@ -109,13 +110,14 @@ func NewFootball(c *graphql.Client) *Football {
|
|||||||
c,
|
c,
|
||||||
"players",
|
"players",
|
||||||
[]string{"football"},
|
[]string{"football"},
|
||||||
),
|
).WithMaxPageSize(100),
|
||||||
Season: graphql.NewQuery[Season, SeasonParams](
|
Season: graphql.NewQuery[Season, SeasonParams](
|
||||||
c,
|
c,
|
||||||
"season",
|
"season",
|
||||||
[]string{"football"},
|
[]string{"football"},
|
||||||
),
|
),
|
||||||
|
|
||||||
So5: NewSo5(c),
|
So5: NewSo5(c),
|
||||||
|
Rivals: NewRivals(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,13 +40,34 @@ type CardSupply struct {
|
|||||||
Unique int `graphql:"unique"`
|
Unique int `graphql:"unique"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClubMembership struct {
|
type MembershipStats struct {
|
||||||
Id graphql.Id `graphql:"id"`
|
Season struct {
|
||||||
Club struct {
|
StartYear int `graphql:"startYear"`
|
||||||
Slug string `graphql:"slug"`
|
} `graphql:"season"`
|
||||||
} `graphql:"club"`
|
Appearances int `graphql:"appearances"`
|
||||||
StartDate time.Time `graphql:"startDate"`
|
Goals int `graphql:"goals"`
|
||||||
EndDate *time.Time `graphql:"endDate"`
|
Assists int `graphql:"assists"`
|
||||||
|
YellowCards int `graphql:"yellowCards"`
|
||||||
|
RedCards int `graphql:"redCards"`
|
||||||
|
MinutesPlayed int `graphql:"minutesPlayed"`
|
||||||
|
SubstituteIn int `graphql:"substituteIn"`
|
||||||
|
SubstituteOut int `graphql:"substituteOut"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Membership struct {
|
||||||
|
Id graphql.Id `graphql:"id"`
|
||||||
|
StartDate time.Time `graphql:"startDate"`
|
||||||
|
EndDate *time.Time `graphql:"endDate"`
|
||||||
|
MembershipTeam struct {
|
||||||
|
TypeName string `graphql:"__typename"`
|
||||||
|
Club struct {
|
||||||
|
Slug string `graphql:"slug"`
|
||||||
|
} `graphql:"... on Club"`
|
||||||
|
NationalTeam struct {
|
||||||
|
Slug string `graphql:"slug"`
|
||||||
|
} `graphql:"... on NationalTeam"`
|
||||||
|
} `graphql:"membershipTeam"`
|
||||||
|
//Stats []MembershipStats `graphql:"stats"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
@ -68,8 +89,8 @@ type Player struct {
|
|||||||
BirthDate time.Time `graphql:"birthDate"`
|
BirthDate time.Time `graphql:"birthDate"`
|
||||||
CardPositions []types.Position `graphql:"cardPositions"`
|
CardPositions []types.Position `graphql:"cardPositions"`
|
||||||
CardSupply []CardSupply `graphql:"cardSupply"`
|
CardSupply []CardSupply `graphql:"cardSupply"`
|
||||||
ClubMemberships []ClubMembership `graphql:"clubMemberships"`
|
|
||||||
Id graphql.Id `graphql:"id"`
|
Id graphql.Id `graphql:"id"`
|
||||||
|
Memberships []Membership `graphql:"memberships"`
|
||||||
Slug string `graphql:"slug"`
|
Slug string `graphql:"slug"`
|
||||||
DisplayName string `graphql:"displayName"`
|
DisplayName string `graphql:"displayName"`
|
||||||
FirstName string `graphql:"firstName"`
|
FirstName string `graphql:"firstName"`
|
||||||
|
17
football/rivals.go
Normal file
17
football/rivals.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package football
|
||||||
|
|
||||||
|
import "git.lehouerou.net/laurent/sorare/graphql"
|
||||||
|
|
||||||
|
type Rivals struct {
|
||||||
|
c *graphql.Client
|
||||||
|
|
||||||
|
PastGames *graphql.PaginatedQuery[RivalsGame, graphql.EmptyParams]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRivals(c *graphql.Client) *Rivals {
|
||||||
|
return &Rivals{
|
||||||
|
c: c,
|
||||||
|
|
||||||
|
PastGames: graphql.NewPaginatedQuery[RivalsGame, graphql.EmptyParams](c, "pastGamesPaginated", []string{"football", "rivals"}).WithOverrideComplexity(107),
|
||||||
|
}
|
||||||
|
}
|
32
football/rivals_game.go
Normal file
32
football/rivals_game.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package football
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.lehouerou.net/laurent/sorare/graphql"
|
||||||
|
"git.lehouerou.net/laurent/sorare/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RivalsDraftableObject struct {
|
||||||
|
Id graphql.Id `graphql:"id"`
|
||||||
|
CapValue float64 `graphql:"capValue"`
|
||||||
|
Player struct {
|
||||||
|
Slug string `graphql:"slug"`
|
||||||
|
ActiveClub struct {
|
||||||
|
Slug string `graphql:"slug"`
|
||||||
|
}
|
||||||
|
ActiveNationalTeam struct {
|
||||||
|
Slug string `graphql:"slug"`
|
||||||
|
}
|
||||||
|
} `graphql:"player"`
|
||||||
|
Position types.Position `graphql:"position"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RivalsGame struct {
|
||||||
|
Id graphql.Id `graphql:"id"`
|
||||||
|
Slug string `graphql:"slug"`
|
||||||
|
Cap int `graphql:"cap"`
|
||||||
|
DraftablePlayers []RivalsDraftableObject `graphql:"draftablePlayers"`
|
||||||
|
FormationKnown bool `graphql:"formationKnown"`
|
||||||
|
Game struct {
|
||||||
|
Id graphql.Id `graphql:"id"`
|
||||||
|
} `graphql:"game"`
|
||||||
|
}
|
@ -22,28 +22,28 @@ func NewSo5(c *graphql.Client) *So5 {
|
|||||||
So5Fixture: graphql.NewQuery[So5Fixture, So5FixtureParams](
|
So5Fixture: graphql.NewQuery[So5Fixture, So5FixtureParams](
|
||||||
c,
|
c,
|
||||||
"so5Fixture",
|
"so5Fixture",
|
||||||
[]string{"football", "so5"},
|
[]string{"so5"},
|
||||||
),
|
),
|
||||||
So5Fixtures: graphql.NewPaginatedQuery[So5Fixture, So5FixturesParams](
|
So5Fixtures: graphql.NewPaginatedQuery[So5Fixture, So5FixturesParams](
|
||||||
c,
|
c,
|
||||||
"so5Fixtures",
|
"so5Fixtures",
|
||||||
[]string{"football", "so5"},
|
[]string{"so5"},
|
||||||
),
|
),
|
||||||
So5Score: graphql.NewQuery[So5Score, graphql.IdParams](
|
So5Score: graphql.NewQuery[So5Score, graphql.IdParams](
|
||||||
c,
|
c,
|
||||||
"so5Score",
|
"so5Score",
|
||||||
[]string{"football", "so5"},
|
[]string{"so5"},
|
||||||
),
|
),
|
||||||
So5Leaderboard: graphql.NewQuery[So5Leaderboard, graphql.SlugParams](
|
So5Leaderboard: graphql.NewQuery[So5Leaderboard, graphql.SlugParams](
|
||||||
c,
|
c,
|
||||||
"so5Leaderboard",
|
"so5Leaderboard",
|
||||||
[]string{"football", "so5"},
|
[]string{"so5"},
|
||||||
),
|
),
|
||||||
|
|
||||||
UpcomingLeaderboards: graphql.NewQuery[[]So5Leaderboard, UpcomingLeaderboardsParams](
|
UpcomingLeaderboards: graphql.NewQuery[[]So5Leaderboard, UpcomingLeaderboardsParams](
|
||||||
c,
|
c,
|
||||||
"upcomingLeaderboards",
|
"upcomingLeaderboards",
|
||||||
[]string{"football", "so5"},
|
[]string{"so5"},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
go.mod
12
go.mod
@ -9,17 +9,17 @@ require (
|
|||||||
github.com/llehouerou/go-graphql-client v0.9.7
|
github.com/llehouerou/go-graphql-client v0.9.7
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/rs/zerolog v1.32.0
|
github.com/rs/zerolog v1.32.0
|
||||||
github.com/shopspring/decimal v1.3.1
|
github.com/shopspring/decimal v1.4.0
|
||||||
github.com/xlzd/gotp v0.1.0
|
github.com/xlzd/gotp v0.1.0
|
||||||
golang.org/x/crypto v0.21.0
|
golang.org/x/crypto v0.22.0
|
||||||
golang.org/x/time v0.5.0
|
golang.org/x/time v0.5.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
golang.org/x/net v0.21.0 // indirect
|
golang.org/x/net v0.24.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
golang.org/x/sys v0.19.0 // indirect
|
||||||
nhooyr.io/websocket v1.8.10 // indirect
|
nhooyr.io/websocket v1.8.11 // indirect
|
||||||
)
|
)
|
||||||
|
12
go.sum
12
go.sum
@ -22,6 +22,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@ -30,19 +32,29 @@ github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
|||||||
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
|
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
|
||||||
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
|
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
|
||||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
|
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
|
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
|
||||||
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
||||||
|
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
|
||||||
|
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
||||||
|
@ -57,10 +57,10 @@ type JwtToken struct {
|
|||||||
|
|
||||||
type SignIn struct {
|
type SignIn struct {
|
||||||
CurrentUser struct {
|
CurrentUser struct {
|
||||||
Slug string
|
Slug string
|
||||||
JwtToken JwtToken `graphql:"jwtToken(aud: $aud)"`
|
|
||||||
} `graphql:"currentUser"`
|
} `graphql:"currentUser"`
|
||||||
OtpSessionChallenge string
|
OtpSessionChallenge string
|
||||||
|
JwtToken JwtToken `graphql:"jwtToken(aud: $aud)"`
|
||||||
Errors []struct {
|
Errors []struct {
|
||||||
Message string
|
Message string
|
||||||
} `graphql:"errors"`
|
} `graphql:"errors"`
|
||||||
@ -116,12 +116,12 @@ func (c *Client) getNewToken(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(res.Errors) == 0 {
|
if len(res.Errors) == 0 {
|
||||||
if res.CurrentUser.JwtToken.Token == "" {
|
if res.JwtToken.Token == "" {
|
||||||
return JwtToken{}, errors.New(
|
return JwtToken{}, errors.New(
|
||||||
"no errors & no jwt token returned on email/password mutation",
|
"no errors & no jwt token returned on email/password mutation",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return res.CurrentUser.JwtToken, nil
|
return res.JwtToken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.Errors[0].Message == "invalid" {
|
if res.Errors[0].Message == "invalid" {
|
||||||
@ -146,10 +146,10 @@ func (c *Client) getNewToken(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(resOtp.Errors) == 0 {
|
if len(resOtp.Errors) == 0 {
|
||||||
if resOtp.CurrentUser.JwtToken.Token == "" {
|
if resOtp.JwtToken.Token == "" {
|
||||||
return JwtToken{}, errors.New("no errors & no jwt token returned on otp mutation")
|
return JwtToken{}, errors.New("no errors & no jwt token returned on otp mutation")
|
||||||
}
|
}
|
||||||
return resOtp.CurrentUser.JwtToken, nil
|
return resOtp.JwtToken, nil
|
||||||
}
|
}
|
||||||
return JwtToken{}, errors.Errorf("validating otp: %s", resOtp.Errors[0].Message)
|
return JwtToken{}, errors.Errorf("validating otp: %s", resOtp.Errors[0].Message)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package graphql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -22,9 +23,11 @@ type Client struct {
|
|||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
gql *graphql.Client
|
gql *graphql.Client
|
||||||
rl *rate.Limiter
|
rl *rate.Limiter
|
||||||
|
apirl *rate.Limiter
|
||||||
lock *sync.Mutex
|
lock *sync.Mutex
|
||||||
authenticated bool
|
authenticated bool
|
||||||
token JwtToken
|
token JwtToken
|
||||||
|
apiKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(httpclient *http.Client, baseUrl string) *Client {
|
func NewClient(httpclient *http.Client, baseUrl string) *Client {
|
||||||
@ -32,6 +35,7 @@ func NewClient(httpclient *http.Client, baseUrl string) *Client {
|
|||||||
httpClient: httpclient,
|
httpClient: httpclient,
|
||||||
gql: graphql.NewClient(baseUrl, httpclient),
|
gql: graphql.NewClient(baseUrl, httpclient),
|
||||||
rl: rate.NewLimiter(rate.Every(rateLimitPeriod), rateLimitBurst),
|
rl: rate.NewLimiter(rate.Every(rateLimitPeriod), rateLimitBurst),
|
||||||
|
apirl: rate.NewLimiter(rate.Every(rateLimitPeriod/10), rateLimitBurst*10),
|
||||||
lock: &sync.Mutex{},
|
lock: &sync.Mutex{},
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
}
|
}
|
||||||
@ -50,12 +54,18 @@ func (c *Client) SetJWTToken(token JwtToken, audience string) {
|
|||||||
c.authenticated = true
|
c.authenticated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) MaxComplexity() int {
|
func (c *Client) SetApiKey(apiKey string) {
|
||||||
if c.authenticated {
|
c.gql = c.gql.WithRequestModifier(func(request *http.Request) {
|
||||||
|
request.Header.Set("APIKEY", apiKey)
|
||||||
|
})
|
||||||
|
c.apiKey = apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) MaxQueryComplexity() int {
|
||||||
|
if c.apiKey != "" || c.authenticated {
|
||||||
return MaxAuthenticatedQueryComplexity
|
return MaxAuthenticatedQueryComplexity
|
||||||
} else {
|
|
||||||
return MaxAnonymousQueryComplexity
|
|
||||||
}
|
}
|
||||||
|
return MaxAnonymousQueryComplexity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ConstructRawQuery(
|
func (c *Client) ConstructRawQuery(
|
||||||
@ -71,9 +81,16 @@ func (c *Client) Query(
|
|||||||
variables interface{},
|
variables interface{},
|
||||||
options ...graphql.Option,
|
options ...graphql.Option,
|
||||||
) error {
|
) error {
|
||||||
err := c.rl.Wait(ctx)
|
if c.apiKey != "" {
|
||||||
if err != nil {
|
err := c.apirl.Wait(ctx)
|
||||||
return errors.Wrap(err, "waiting for rate limit")
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "waiting for rate limit")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := c.rl.Wait(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "waiting for rate limit")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
@ -86,9 +103,16 @@ func (c *Client) QueryRaw(
|
|||||||
variables interface{},
|
variables interface{},
|
||||||
options ...graphql.Option,
|
options ...graphql.Option,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
err := c.rl.Wait(ctx)
|
if c.apiKey != "" {
|
||||||
if err != nil {
|
err := c.apirl.Wait(ctx)
|
||||||
return nil, errors.Wrap(err, "waiting for rate limit")
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "waiting for rate limit")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := c.rl.Wait(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "waiting for rate limit")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
@ -101,11 +125,67 @@ func (c *Client) Mutate(
|
|||||||
variables interface{},
|
variables interface{},
|
||||||
options ...graphql.Option,
|
options ...graphql.Option,
|
||||||
) error {
|
) error {
|
||||||
err := c.rl.Wait(ctx)
|
if c.apiKey != "" {
|
||||||
if err != nil {
|
err := c.apirl.Wait(ctx)
|
||||||
return errors.Wrap(err, "waiting for rate limit")
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "waiting for rate limit")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := c.rl.Wait(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "waiting for rate limit")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
return c.gql.Mutate(ctx, q, variables, options...)
|
return c.gql.Mutate(ctx, q, variables, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTypeComplexity(t reflect.Type) int {
|
||||||
|
var checkStruct func(t reflect.Type, complexity *int)
|
||||||
|
checkStruct = func(t reflect.Type, complexity *int) {
|
||||||
|
if t.Kind() != reflect.Struct {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
tag := field.Tag.Get("graphql")
|
||||||
|
if field.Type.Kind() == reflect.Struct {
|
||||||
|
*complexity++
|
||||||
|
if len(tag) > 6 && tag[:6] == "... on" {
|
||||||
|
*complexity--
|
||||||
|
}
|
||||||
|
checkStruct(field.Type, complexity)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if field.Type.Kind() == reflect.Slice {
|
||||||
|
if isSimpleType(field.Type.Elem()) {
|
||||||
|
*complexity++
|
||||||
|
} else {
|
||||||
|
*complexity++
|
||||||
|
tmpcomplexity := 0
|
||||||
|
checkStruct(field.Type.Elem(), &tmpcomplexity)
|
||||||
|
*complexity += tmpcomplexity * 10
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tag != "" && tag != "__typename" {
|
||||||
|
*complexity++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
complexity := 0
|
||||||
|
checkStruct(t, &complexity)
|
||||||
|
return complexity
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSimpleType(t reflect.Type) bool {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||||
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||||
|
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,8 +3,6 @@ package graphql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PaginatedQuery[ResultType any, Params any] struct {
|
type PaginatedQuery[ResultType any, Params any] struct {
|
||||||
@ -14,6 +12,8 @@ type PaginatedQuery[ResultType any, Params any] struct {
|
|||||||
|
|
||||||
additionalPayloadParams map[string]interface{}
|
additionalPayloadParams map[string]interface{}
|
||||||
additionalQueryParams map[string]interface{}
|
additionalQueryParams map[string]interface{}
|
||||||
|
|
||||||
|
overrideComplexity int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPaginatedQuery[ResultType any, Params any](
|
func NewPaginatedQuery[ResultType any, Params any](
|
||||||
@ -27,6 +27,7 @@ func NewPaginatedQuery[ResultType any, Params any](
|
|||||||
containerLayers: containerLayers,
|
containerLayers: containerLayers,
|
||||||
additionalPayloadParams: make(map[string]interface{}),
|
additionalPayloadParams: make(map[string]interface{}),
|
||||||
additionalQueryParams: make(map[string]interface{}),
|
additionalQueryParams: make(map[string]interface{}),
|
||||||
|
overrideComplexity: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +47,11 @@ func (pq *PaginatedQuery[ResultType, Params]) WithQueryParam(
|
|||||||
return pq
|
return pq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pq *PaginatedQuery[ResultType, Params]) WithOverrideComplexity(complexity int) *PaginatedQuery[ResultType, Params] {
|
||||||
|
pq.overrideComplexity = complexity
|
||||||
|
return pq
|
||||||
|
}
|
||||||
|
|
||||||
type PaginatedQueryGetOptions struct {
|
type PaginatedQueryGetOptions struct {
|
||||||
Limit int
|
Limit int
|
||||||
}
|
}
|
||||||
@ -70,18 +76,26 @@ func (pq *PaginatedQuery[ResultType, Params]) Get(
|
|||||||
var res []ResultType
|
var res []ResultType
|
||||||
after := ""
|
after := ""
|
||||||
var noop ResultType
|
var noop ResultType
|
||||||
pageSize := (pq.c.MaxComplexity() - 9) / (GetComplexity(reflect.TypeOf(noop)) + 1)
|
complexity := pq.overrideComplexity
|
||||||
log.Debug().Msgf("using page size %d", pageSize)
|
if complexity == 0 {
|
||||||
|
complexity = GetTypeComplexity(reflect.TypeOf(noop))
|
||||||
|
}
|
||||||
|
maxPageSize := (pq.c.MaxQueryComplexity() - 7 - len(pq.containerLayers)) / complexity
|
||||||
for {
|
for {
|
||||||
|
pageSize := maxPageSize
|
||||||
|
if opts.Limit > 0 {
|
||||||
|
remaining := opts.Limit - len(res)
|
||||||
|
if remaining < pageSize {
|
||||||
|
pageSize = remaining
|
||||||
|
}
|
||||||
|
}
|
||||||
page, pi, err := pq.getPage(ctx, params, after, pageSize)
|
page, pi, err := pq.getPage(ctx, params, after, pageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, item := range page {
|
res = append(res, page...)
|
||||||
res = append(res, item)
|
if opts.Limit > 0 && len(res) >= opts.Limit {
|
||||||
if opts.Limit > 0 && len(res) >= opts.Limit {
|
return res[:opts.Limit], nil
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !pi.HasNextPage {
|
if !pi.HasNextPage {
|
||||||
break
|
break
|
||||||
@ -125,35 +139,3 @@ func (pq *PaginatedQuery[ResultType, Params]) getPage(
|
|||||||
}
|
}
|
||||||
return res.Nodes, res.PageInfo, nil
|
return res.Nodes, res.PageInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetComplexity(t reflect.Type) int {
|
|
||||||
var checkStruct func(t reflect.Type, complexity *int)
|
|
||||||
checkStruct = func(t reflect.Type, complexity *int) {
|
|
||||||
if t.Kind() != reflect.Struct {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
field := t.Field(i)
|
|
||||||
if field.Type.Kind() == reflect.Struct {
|
|
||||||
*complexity++
|
|
||||||
tag := field.Tag.Get("graphql")
|
|
||||||
if len(tag) > 6 && tag[:6] == "... on" {
|
|
||||||
*complexity--
|
|
||||||
}
|
|
||||||
checkStruct(field.Type, complexity)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if field.Type.Kind() == reflect.Slice {
|
|
||||||
*complexity++
|
|
||||||
tmpcomplexity := 0
|
|
||||||
checkStruct(field.Type.Elem(), &tmpcomplexity)
|
|
||||||
*complexity += tmpcomplexity * 10
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
*complexity++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
complexity := 0
|
|
||||||
checkStruct(t, &complexity)
|
|
||||||
return complexity
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ package graphql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -15,6 +16,8 @@ type Query[ReturnType any, Params any] struct {
|
|||||||
|
|
||||||
additionalPayloadParams map[string]interface{}
|
additionalPayloadParams map[string]interface{}
|
||||||
additionalQueryParams map[string]interface{}
|
additionalQueryParams map[string]interface{}
|
||||||
|
|
||||||
|
maxPageSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewQuery[ReturnType any, Params any](
|
func NewQuery[ReturnType any, Params any](
|
||||||
@ -29,6 +32,7 @@ func NewQuery[ReturnType any, Params any](
|
|||||||
|
|
||||||
additionalPayloadParams: make(map[string]interface{}),
|
additionalPayloadParams: make(map[string]interface{}),
|
||||||
additionalQueryParams: make(map[string]interface{}),
|
additionalQueryParams: make(map[string]interface{}),
|
||||||
|
maxPageSize: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +52,27 @@ func (r *Query[ReturnType, Params]) WithQueryParam(
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Query[ReturnType, Params]) WithMaxPageSize(maxPageSize int) *Query[ReturnType, Params] {
|
||||||
|
r.maxPageSize = maxPageSize
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Query[ReturnType, Params]) GetPageSize() int {
|
||||||
|
t := reflect.TypeOf((*ReturnType)(nil)).Elem()
|
||||||
|
if t.Kind() == reflect.Slice {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
complexity := GetTypeComplexity(t)
|
||||||
|
maxComplexity := r.c.MaxQueryComplexity()
|
||||||
|
layers := len(r.containerLayers)
|
||||||
|
computedPageSize := (maxComplexity - layers - 1) / complexity
|
||||||
|
|
||||||
|
if r.maxPageSize > 0 && r.maxPageSize < computedPageSize {
|
||||||
|
return r.maxPageSize
|
||||||
|
}
|
||||||
|
return computedPageSize
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Query[ReturnType, Params]) Get(ctx context.Context, params Params) (ReturnType, error) {
|
func (r *Query[ReturnType, Params]) Get(ctx context.Context, params Params) (ReturnType, error) {
|
||||||
|
|
||||||
paramsMap := convertParamsToMap(params)
|
paramsMap := convertParamsToMap(params)
|
||||||
|
50
mutations/buy_shop_item.go
Normal file
50
mutations/buy_shop_item.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package mutations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
gql "github.com/llehouerou/go-graphql-client"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"git.lehouerou.net/laurent/sorare/graphql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type buyShopItemInput struct {
|
||||||
|
ClientMutationId string `json:"clientMutationId"`
|
||||||
|
ShopItemId gql.ID `json:"shopItemId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuyShopItemParams struct {
|
||||||
|
Input buyShopItemInput `graphql:"input"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mutations) newBuyShopItemMutation() *graphql.Mutation[BaseMutationPayload, BuyShopItemParams] {
|
||||||
|
return graphql.NewMutation[BaseMutationPayload, BuyShopItemParams](
|
||||||
|
m.c,
|
||||||
|
"buyShopItem",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mutations) BuyShopItem(
|
||||||
|
ctx context.Context,
|
||||||
|
shopItemId string,
|
||||||
|
) error {
|
||||||
|
res, err := m.newBuyShopItemMutation().Execute(ctx, BuyShopItemParams{
|
||||||
|
Input: buyShopItemInput{
|
||||||
|
ClientMutationId: GetRandomId(),
|
||||||
|
ShopItemId: gql.ID(shopItemId),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "executing buyShopItem mutation")
|
||||||
|
}
|
||||||
|
if res.Errors != nil {
|
||||||
|
return errors.Wrap(
|
||||||
|
errors.New(res.Errors[0].Message),
|
||||||
|
"executing buyShopItem mutation",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
19
shop_item.go
19
shop_item.go
@ -1,16 +1,27 @@
|
|||||||
package sorare
|
package sorare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type ShopItem struct {
|
type ShopItem struct {
|
||||||
|
TypeName string `graphql:"__typename"`
|
||||||
ShopItemInterface struct {
|
ShopItemInterface struct {
|
||||||
Id string `graphql:"id"`
|
Id string `graphql:"id"`
|
||||||
Name string `graphql:"name"`
|
Name string `graphql:"name"`
|
||||||
Price int `graphql:"price"`
|
Price int `graphql:"price"`
|
||||||
|
MyBalance int `graphql:"myBalance"`
|
||||||
|
LimitPerUser int `graphql:"limitPerUser"`
|
||||||
|
MyLimitResetAt time.Time `graphql:"myLimitResetAt"`
|
||||||
|
MyPurchasesCount int `graphql:"myPurchasesCount"`
|
||||||
} `graphql:"... on ShopItemInterface"`
|
} `graphql:"... on ShopItemInterface"`
|
||||||
JerseyShopItem struct {
|
JerseyShopItem struct {
|
||||||
CurrentStockCount int `graphql:"currentStockCount"`
|
CurrentStockCount int `graphql:"currentStockCount"`
|
||||||
InitialStockCount int `graphql:"initialStockCount"`
|
InitialStockCount int `graphql:"initialStockCount"`
|
||||||
LimitPerUser int `graphql:"limitPerUser"`
|
|
||||||
} `graphql:"... on JerseyShopItem"`
|
} `graphql:"... on JerseyShopItem"`
|
||||||
|
LevelUpShopItem struct {
|
||||||
|
Rarity string `graphql:"rarity"`
|
||||||
|
} `graphql:"... on LevelUpShopItem"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShopItemType string
|
type ShopItemType string
|
||||||
|
6
user.go
6
user.go
@ -12,8 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type baseUser struct {
|
type baseUser struct {
|
||||||
Active bool `graphql:"active"`
|
Active bool `graphql:"active"`
|
||||||
CardCounts struct {
|
FootballCardCounts struct {
|
||||||
Common int `graphql:"common"`
|
Common int `graphql:"common"`
|
||||||
CustomSeries int `graphql:"customSeries"`
|
CustomSeries int `graphql:"customSeries"`
|
||||||
Limited int `graphql:"limited"`
|
Limited int `graphql:"limited"`
|
||||||
@ -21,7 +21,7 @@ type baseUser struct {
|
|||||||
SuperRare int `graphql:"superRare"`
|
SuperRare int `graphql:"superRare"`
|
||||||
Unique int `graphql:"unique"`
|
Unique int `graphql:"unique"`
|
||||||
Total int `graphql:"total"`
|
Total int `graphql:"total"`
|
||||||
} `graphql:"cardCounts"`
|
} `graphql:"footballCardCounts"`
|
||||||
CreatedAt time.Time `graphql:"createdAt"`
|
CreatedAt time.Time `graphql:"createdAt"`
|
||||||
EthVaultId int `graphql:"ethVaultId"`
|
EthVaultId int `graphql:"ethVaultId"`
|
||||||
EthereumAddress string `graphql:"ethereumAddress"`
|
EthereumAddress string `graphql:"ethereumAddress"`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user