Skip to content

Commit ff720ad

Browse files
authored
Merge pull request #5 from sumo-mcp/workspace
Add GetRikishiAPI
2 parents 37d219c + db32caa commit ff720ad

5 files changed

Lines changed: 358 additions & 0 deletions

File tree

client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
// Client is a client for the Sumo API.
1414
type Client interface {
1515
SearchRikishisAPI
16+
GetRikishiAPI
1617
ListRankChangesAPI
1718
ListShikonaChangesAPI
1819
ListMeasurementChangesAPI

get_rikishi.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package sumoapi
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/url"
7+
)
8+
9+
// GetRikishiAPI defines the methods available for retrieving a single Rikishi.
10+
type GetRikishiAPI interface {
11+
// GetRikishi calls the GET /api/rikishi/{rikishiID} endpoint.
12+
GetRikishi(ctx context.Context, req GetRikishiRequest) (*Rikishi, error)
13+
}
14+
15+
// GetRikishiRequest represents the request parameters for the GetRikishi method.
16+
type GetRikishiRequest struct {
17+
RikishiID int `json:"rikishiId" jsonschema:"The unique identifier of the Rikishi to retrieve. Example: 45 = Terunofuji"`
18+
IncludeRanks bool `json:"includeRanks,omitempty" jsonschema:"Whether to include rank records over time in the Rikishi data."`
19+
IncludeShikonas bool `json:"includeShikonas,omitempty" jsonschema:"Whether to include shikona (ring name) records over time in the Rikishi data."`
20+
IncludeMeasurements bool `json:"includeMeasurements,omitempty" jsonschema:"Whether to include measurement records over time in the Rikishi data."`
21+
}
22+
23+
func (c *client) GetRikishi(ctx context.Context, req GetRikishiRequest) (*Rikishi, error) {
24+
query := make(url.Values)
25+
if req.IncludeMeasurements {
26+
query.Set("measurements", "true")
27+
}
28+
if req.IncludeRanks {
29+
query.Set("ranks", "true")
30+
}
31+
if req.IncludeShikonas {
32+
query.Set("shikonas", "true")
33+
}
34+
path := fmt.Sprintf("/rikishi/%d", req.RikishiID)
35+
return getObject[Rikishi](ctx, c, path, query)
36+
}

get_rikishi_test.go

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
package sumoapi_test
2+
3+
import (
4+
"context"
5+
"io"
6+
"net/http"
7+
"strings"
8+
"testing"
9+
10+
. "github.com/onsi/gomega"
11+
12+
"github.com/sumo-mcp/sumoapi-go"
13+
)
14+
15+
func TestClient_GetRikishi(t *testing.T) {
16+
t.Run("get rikishi by ID", func(t *testing.T) {
17+
g := NewWithT(t)
18+
19+
mockResp := `{
20+
"id": 45,
21+
"shikonaEn": "Terunofuji",
22+
"shikonaJp": "照ノ富士",
23+
"currentRank": "Yokozuna",
24+
"heya": "Isegahama"
25+
}`
26+
27+
transport := &mockTransport{
28+
validateRequest: func(req *http.Request) error {
29+
g.Expect(req.Method).To(Equal(http.MethodGet))
30+
g.Expect(req.URL.Scheme).To(Equal("https"))
31+
g.Expect(req.URL.Host).To(Equal("sumo-api.com"))
32+
g.Expect(req.URL.Path).To(Equal("/api/rikishi/45"))
33+
g.Expect(req.URL.Query().Has("measurements")).To(BeFalse())
34+
g.Expect(req.URL.Query().Has("ranks")).To(BeFalse())
35+
g.Expect(req.URL.Query().Has("shikonas")).To(BeFalse())
36+
return nil
37+
},
38+
response: &http.Response{
39+
StatusCode: http.StatusOK,
40+
Body: io.NopCloser(strings.NewReader(mockResp)),
41+
},
42+
}
43+
44+
client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport}))
45+
resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
46+
RikishiID: 45,
47+
})
48+
49+
g.Expect(err).ToNot(HaveOccurred())
50+
g.Expect(resp).ToNot(BeNil())
51+
g.Expect(resp.ID).To(Equal(45))
52+
g.Expect(resp.ShikonaEnglish).To(Equal("Terunofuji"))
53+
g.Expect(resp.ShikonaJapanese).To(Equal("照ノ富士"))
54+
g.Expect(resp.CurrentRank).To(Equal("Yokozuna"))
55+
g.Expect(resp.Heya).To(Equal("Isegahama"))
56+
})
57+
58+
t.Run("get rikishi with include ranks", func(t *testing.T) {
59+
g := NewWithT(t)
60+
61+
mockResp := `{
62+
"id": 45,
63+
"shikonaEn": "Terunofuji",
64+
"rankHistory": [
65+
{
66+
"id": "202501-45",
67+
"bashoId": "202501",
68+
"rikishiId": 45,
69+
"rank": "Yokozuna 1 East"
70+
}
71+
]
72+
}`
73+
74+
transport := &mockTransport{
75+
validateRequest: func(req *http.Request) error {
76+
g.Expect(req.URL.Path).To(Equal("/api/rikishi/45"))
77+
g.Expect(req.URL.Query().Get("ranks")).To(Equal("true"))
78+
return nil
79+
},
80+
response: &http.Response{
81+
StatusCode: http.StatusOK,
82+
Body: io.NopCloser(strings.NewReader(mockResp)),
83+
},
84+
}
85+
86+
client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport}))
87+
resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
88+
RikishiID: 45,
89+
IncludeRanks: true,
90+
})
91+
92+
g.Expect(err).ToNot(HaveOccurred())
93+
g.Expect(resp).ToNot(BeNil())
94+
g.Expect(resp.RankHistory).To(HaveLen(1))
95+
g.Expect(resp.RankHistory[0].RikishiID).To(Equal(45))
96+
})
97+
98+
t.Run("get rikishi with include shikonas", func(t *testing.T) {
99+
g := NewWithT(t)
100+
101+
mockResp := `{
102+
"id": 45,
103+
"shikonaEn": "Terunofuji",
104+
"shikonaHistory": [
105+
{
106+
"id": "202501-45",
107+
"bashoId": "202501",
108+
"rikishiId": 45,
109+
"shikonaEn": "Terunofuji",
110+
"shikonaJp": "照ノ富士"
111+
}
112+
]
113+
}`
114+
115+
transport := &mockTransport{
116+
validateRequest: func(req *http.Request) error {
117+
g.Expect(req.URL.Path).To(Equal("/api/rikishi/45"))
118+
g.Expect(req.URL.Query().Get("shikonas")).To(Equal("true"))
119+
return nil
120+
},
121+
response: &http.Response{
122+
StatusCode: http.StatusOK,
123+
Body: io.NopCloser(strings.NewReader(mockResp)),
124+
},
125+
}
126+
127+
client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport}))
128+
resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
129+
RikishiID: 45,
130+
IncludeShikonas: true,
131+
})
132+
133+
g.Expect(err).ToNot(HaveOccurred())
134+
g.Expect(resp).ToNot(BeNil())
135+
g.Expect(resp.ShikonaHistory).To(HaveLen(1))
136+
g.Expect(resp.ShikonaHistory[0].RikishiID).To(Equal(45))
137+
})
138+
139+
t.Run("get rikishi with include measurements", func(t *testing.T) {
140+
g := NewWithT(t)
141+
142+
mockResp := `{
143+
"id": 45,
144+
"shikonaEn": "Terunofuji",
145+
"measurementHistory": [
146+
{
147+
"id": "202501-45",
148+
"bashoId": "202501",
149+
"rikishiId": 45,
150+
"height": 193.0,
151+
"weight": 180.0
152+
}
153+
]
154+
}`
155+
156+
transport := &mockTransport{
157+
validateRequest: func(req *http.Request) error {
158+
g.Expect(req.URL.Path).To(Equal("/api/rikishi/45"))
159+
g.Expect(req.URL.Query().Get("measurements")).To(Equal("true"))
160+
return nil
161+
},
162+
response: &http.Response{
163+
StatusCode: http.StatusOK,
164+
Body: io.NopCloser(strings.NewReader(mockResp)),
165+
},
166+
}
167+
168+
client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport}))
169+
resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
170+
RikishiID: 45,
171+
IncludeMeasurements: true,
172+
})
173+
174+
g.Expect(err).ToNot(HaveOccurred())
175+
g.Expect(resp).ToNot(BeNil())
176+
g.Expect(resp.MeasurementHistory).To(HaveLen(1))
177+
g.Expect(resp.MeasurementHistory[0].RikishiID).To(Equal(45))
178+
})
179+
180+
t.Run("get rikishi with all includes", func(t *testing.T) {
181+
g := NewWithT(t)
182+
183+
mockResp := `{
184+
"id": 45,
185+
"shikonaEn": "Terunofuji",
186+
"rankHistory": [],
187+
"shikonaHistory": [],
188+
"measurementHistory": []
189+
}`
190+
191+
transport := &mockTransport{
192+
validateRequest: func(req *http.Request) error {
193+
query := req.URL.Query()
194+
g.Expect(query.Get("ranks")).To(Equal("true"))
195+
g.Expect(query.Get("shikonas")).To(Equal("true"))
196+
g.Expect(query.Get("measurements")).To(Equal("true"))
197+
return nil
198+
},
199+
response: &http.Response{
200+
StatusCode: http.StatusOK,
201+
Body: io.NopCloser(strings.NewReader(mockResp)),
202+
},
203+
}
204+
205+
client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport}))
206+
resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
207+
RikishiID: 45,
208+
IncludeRanks: true,
209+
IncludeShikonas: true,
210+
IncludeMeasurements: true,
211+
})
212+
213+
g.Expect(err).ToNot(HaveOccurred())
214+
g.Expect(resp).ToNot(BeNil())
215+
})
216+
217+
t.Run("context is propagated", func(t *testing.T) {
218+
g := NewWithT(t)
219+
220+
mockResp := `{
221+
"id": 45,
222+
"shikonaEn": "Terunofuji"
223+
}`
224+
225+
type testKey struct{}
226+
ctx := context.WithValue(context.Background(), testKey{}, "test-value")
227+
228+
transport := &mockTransport{
229+
validateRequest: func(req *http.Request) error {
230+
g.Expect(req.Context().Value(testKey{})).To(Equal("test-value"))
231+
return nil
232+
},
233+
response: &http.Response{
234+
StatusCode: http.StatusOK,
235+
Body: io.NopCloser(strings.NewReader(mockResp)),
236+
},
237+
}
238+
239+
client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport}))
240+
_, err := client.GetRikishi(ctx, sumoapi.GetRikishiRequest{
241+
RikishiID: 45,
242+
})
243+
244+
g.Expect(err).ToNot(HaveOccurred())
245+
})
246+
247+
t.Run("http request error", func(t *testing.T) {
248+
g := NewWithT(t)
249+
250+
transport := &mockTransport{
251+
validateRequest: func(req *http.Request) error {
252+
return http.ErrAbortHandler
253+
},
254+
}
255+
256+
client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport}))
257+
resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
258+
RikishiID: 45,
259+
})
260+
261+
g.Expect(err).To(HaveOccurred())
262+
g.Expect(err.Error()).To(ContainSubstring("error making http request"))
263+
g.Expect(resp).To(BeNil())
264+
})
265+
266+
t.Run("invalid JSON response", func(t *testing.T) {
267+
g := NewWithT(t)
268+
269+
transport := &mockTransport{
270+
response: &http.Response{
271+
StatusCode: http.StatusOK,
272+
Body: io.NopCloser(strings.NewReader("not valid json")),
273+
},
274+
}
275+
276+
client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport}))
277+
resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
278+
RikishiID: 45,
279+
})
280+
281+
g.Expect(err).To(HaveOccurred())
282+
g.Expect(err.Error()).To(ContainSubstring("error unmarshaling response body"))
283+
g.Expect(resp).To(BeNil())
284+
})
285+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package integration_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
. "github.com/onsi/gomega"
8+
9+
"github.com/sumo-mcp/sumoapi-go"
10+
)
11+
12+
func TestIntegration_GetRikishi(t *testing.T) {
13+
g := NewWithT(t)
14+
15+
client := sumoapi.New()
16+
17+
resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
18+
RikishiID: 45,
19+
IncludeRanks: true,
20+
IncludeShikonas: true,
21+
IncludeMeasurements: true,
22+
})
23+
24+
g.Expect(err).ToNot(HaveOccurred())
25+
g.Expect(resp).ToNot(BeNil())
26+
g.Expect(resp.ID).To(Equal(45))
27+
g.Expect(resp.ShikonaEnglish).To(Equal("Terunofuji Haruo"))
28+
g.Expect(resp.ShikonaJapanese).To(Equal("照ノ富士 春雄"))
29+
g.Expect(resp.Heya).To(Equal("Isegahama"))
30+
31+
g.Expect(resp.RankHistory).ToNot(BeEmpty())
32+
g.Expect(resp.ShikonaHistory).ToNot(BeEmpty())
33+
g.Expect(resp.MeasurementHistory).ToNot(BeEmpty())
34+
}

tests/integration/list_rank_changes_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ func TestIntegration_ListRankChanges(t *testing.T) {
1414

1515
client := sumoapi.New()
1616

17+
// Here we test a specific Rikishi that had rank changes to make sure the API
18+
// is returning exactly one change when filtering by RikishiID and BashoID.
1719
resp, err := client.ListRankChanges(context.Background(), sumoapi.ListRikishiChangesRequest{
1820
RikishiID: 3081,
1921
BashoID: &sumoapi.BashoID{

0 commit comments

Comments
 (0)