Skip to content

Commit bb2703a

Browse files
authored
feat(auth): add token info command (#8)
* feat(auth): add token info command * fix(cmd): satisfy lint after plexgo upgrade * fix(cmd): remove stale int conversions * fix(cmd): remove remaining pointer conversions * fix(cmd): remove quality pointer conversions * fix(cmd): remove final pointer conversions * chore(ci): trigger fresh workflow run * fix(build): restore unmatched compatibility * fix(auth): address token info review findings * test(auth): add sanitized live Plex fixtures
1 parent 4383eb4 commit bb2703a

30 files changed

Lines changed: 1172 additions & 213 deletions

‎AGENTS.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Project Notes
2+
3+
## Real-Response Test Fixtures
4+
5+
- `internal/auth/testdata/plex_tv_user.json` and `internal/auth/testdata/plex_tv_resources.json` are sanitized fixtures derived from real Plex.tv responses captured on March 6, 2026.
6+
- These fixtures were shaped from local `.env` credentials during development and must not contain live tokens, private IPs, real email addresses, or other secrets.
7+
- When changing Plex auth/resource normalization in `internal/auth/plextv.go`, update the fixtures and the fixture-backed tests in `internal/auth/auth_test.go` to reflect real response shapes.
8+
- Prefer extending this fixture-based approach before adding broader mocks when validating Plex.tv schema changes.
9+
- Apply the same pattern anywhere the CLI depends on external Plex response shapes or other schema-sensitive integrations.
10+
- For relevant changes, prefer adding sanitized real-response fixtures plus normalization/output tests for:
11+
- Plex.tv account, token, and resource payloads
12+
- Plex server library, metadata, history, and stream payloads
13+
- Command output that flattens or reformats external API responses
14+
- When a live response reveals a field shape the current tests do not cover, add or refresh fixtures in the nearest `testdata` directory as part of the change.

‎cmd/plex/main.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ func applyOutputFormat(cli *CLI, format string) {
184184
cli.Watch.Stats.Output = format
185185
cli.Cache.Clear.Output = format
186186
cli.Auth.Servers.Output = format
187+
cli.Auth.TokenInfo.Output = format
187188
}
188189

189190
// loadConfig loads configuration from file and/or environment/cli flags

‎go.mod‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/dogbertdev/plexcli
33
go 1.22
44

55
require (
6-
github.com/LukeHagar/plexgo v0.27.0
6+
github.com/LukeHagar/plexgo v0.28.2
77
github.com/alecthomas/kong v1.13.0
88
github.com/muesli/termenv v0.16.0
99
github.com/olekukonko/tablewriter v1.1.3

‎go.sum‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/LukeHagar/plexgo v0.27.0 h1:Up/CObHhzoxwtVDZfpkJNcoLw98ZQ/mzHW/1f5JTVPs=
22
github.com/LukeHagar/plexgo v0.27.0/go.mod h1:kxd/ulciB3OeABsxL2CYV0MsCEMi9etm8gX2c/thgn0=
3+
github.com/LukeHagar/plexgo v0.28.2 h1:eKugagPqzOuysMx/wpZxxBCd622aq0Xn+oInP1qZZWs=
4+
github.com/LukeHagar/plexgo v0.28.2/go.mod h1:kxd/ulciB3OeABsxL2CYV0MsCEMi9etm8gX2c/thgn0=
35
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
46
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
57
github.com/alecthomas/kong v1.13.0 h1:5e/7XC3ugvhP1DQBmTS+WuHtCbcv44hsohMgcvVxSrA=

‎internal/auth/auth.go‎

Lines changed: 5 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -85,27 +85,8 @@ func (t *TokenAuth) Authenticate(ctx context.Context) (string, error) {
8585
return "", ErrNoCredentials
8686
}
8787

88-
client := &http.Client{Timeout: DefaultTimeout}
89-
req, err := http.NewRequestWithContext(ctx, "GET", PlexTVURL+"/user", nil)
90-
if err != nil {
91-
return "", fmt.Errorf("%w: %v", ErrInvalidToken, err)
92-
}
93-
94-
req.Header.Set("Accept", "application/json")
95-
req.Header.Set("X-Plex-Token", t.token)
96-
req.Header.Set("X-Plex-Client-Identifier", DefaultClientID)
97-
req.Header.Set("X-Plex-Product", DefaultProduct)
98-
req.Header.Set("X-Plex-Version", DefaultVersion)
99-
req.Header.Set("X-Plex-Platform", DefaultPlatform)
100-
101-
resp, err := client.Do(req)
102-
if err != nil {
103-
return "", fmt.Errorf("%w: %v", ErrInvalidToken, err)
104-
}
105-
defer resp.Body.Close()
106-
107-
if resp.StatusCode != StatusOK {
108-
return "", fmt.Errorf("%w: received status %d", ErrInvalidToken, resp.StatusCode)
88+
if _, err := getTokenDetails(ctx, t.token); err != nil {
89+
return "", err
10990
}
11091

11192
return t.token, nil
@@ -358,44 +339,12 @@ func PollPINToken(ctx context.Context, pinID int64, clientID string, pollInterva
358339
}
359340

360341
func DiscoverServers(ctx context.Context, token string) ([]ServerResource, error) {
361-
if token == "" {
362-
return nil, ErrNoCredentials
363-
}
364-
365-
req, err := http.NewRequestWithContext(ctx, "GET", PlexTVURL+"/resources?includeHttps=1", nil)
342+
resources, err := getServerResources(ctx, token)
366343
if err != nil {
367-
return nil, fmt.Errorf("%w: failed to create discover request: %v", ErrAuthFailed, err)
368-
}
369-
req.Header.Set("Accept", "application/json")
370-
req.Header.Set("X-Plex-Token", token)
371-
req.Header.Set("X-Plex-Client-Identifier", DefaultClientID)
372-
req.Header.Set("X-Plex-Product", DefaultProduct)
373-
req.Header.Set("X-Plex-Version", DefaultVersion)
374-
req.Header.Set("X-Plex-Platform", DefaultPlatform)
375-
376-
resp, err := (&http.Client{Timeout: DefaultTimeout}).Do(req)
377-
if err != nil {
378-
return nil, fmt.Errorf("%w: failed to discover servers: %v", ErrAuthFailed, err)
379-
}
380-
defer resp.Body.Close()
381-
382-
if resp.StatusCode != StatusOK {
383-
return nil, fmt.Errorf("%w: failed to discover servers (status %d)", ErrAuthFailed, resp.StatusCode)
384-
}
385-
386-
var resources []ServerResource
387-
if err := json.NewDecoder(resp.Body).Decode(&resources); err != nil {
388-
return nil, fmt.Errorf("%w: failed to decode server resources: %v", ErrAuthFailed, err)
389-
}
390-
391-
servers := make([]ServerResource, 0, len(resources))
392-
for _, resource := range resources {
393-
if strings.Contains(resource.Product, "Plex Media Server") {
394-
servers = append(servers, resource)
395-
}
344+
return nil, err
396345
}
397346

398-
return servers, nil
347+
return normalizeServerResources(resources), nil
399348
}
400349

401350
func PreferredServerURI(server ServerResource, preferLocal bool) (string, bool) {

0 commit comments

Comments
 (0)