Last Updated: 2026-02-05
This is a well-structured Go CLI for the Rebrickable LEGO API. The architecture follows good separation of concerns with distinct cmd and api packages. However, there are several areas that need improvement.
Progress: 7 issues fixed, 3 remaining.
Location: cli/cmd/user.go:61-66
Status: This issue has been resolved. The login function now properly returns errors:
if err != nil {
return nil, fmt.Errorf("login request failed: %w", err)
}
if resp.StatusCode() != 200 {
return nil, fmt.Errorf("login failed with status %d", resp.StatusCode())
}Status: Resolved during API client refactoring. All API methods now return error and callers handle them properly.
Status: Resolved. Command handlers now check and return errors from API calls and JSON marshaling.
Location: cli/cmd/api/api.go:100-123
Status: This issue has been resolved. Proper typed structs now replace the untyped map[string]any:
type Set struct {
SetNum string `json:"set_num"`
Name string `json:"name"`
Year int `json:"year"`
ThemeID int `json:"theme_id"`
NumParts int `json:"num_parts"`
SetImgURL string `json:"set_img_url"`
SetURL string `json:"set_url"`
LastModifiedDt string `json:"last_modified_dt"`
}
type UserSet struct {
ListID int `json:"list_id"`
Quantity int `json:"quantity"`
IncludeSpares bool `json:"include_spares"`
Set Set `json:"set"`
}
type SetsResponse struct {
Count int `json:"count"`
Next string `json:"next"`
Previous string `json:"previous"`
Results []UserSet `json:"results"`
}Location: cli/cmd/sets.go (8 places)
authToken := cmd.Context().Value(AuthToken).(string)
apiKey := cmd.Context().Value(ApiKey).(string)Problem: If context values are missing, this panics.
Fix: Use safe type assertions with error handling:
authToken, ok := cmd.Context().Value(AuthToken).(string)
if !ok || authToken == "" {
return fmt.Errorf("not authenticated")
}Location: cli/cmd/user.go:17-20
const (
ApiKey = "api_key"
AuthToken = "auth_token"
)Problem: String keys can collide with other packages. Go idiom is to use unexported typed keys.
Fix:
type contextKey string
const (
apiKeyKey contextKey = "api_key"
authTokenKey contextKey = "auth_token"
)Status: Resolved. The api.Client struct now encapsulates the HTTP client, created once via api.NewClient(). Command handlers use newAPIClient(cmd) helper.
Status: Resolved. Headers are now configured once in NewClient():
func NewClient(apiKey, authToken string) *Client {
http := resty.New().
SetBaseURL(apiBaseURI).
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("key %s", apiKey))
return &Client{http: http, authToken: authToken}
}Status: Resolved during API client refactoring. Now uses direct concatenation and fmt.Printf.
Location: cli/cmd/sets.go:12-13
var setNumber string
var setListName stringProblem: Module-level mutable variables can cause issues with testing and concurrent usage.
Location: cli/cmd/sets.go:126-132
func adjustedSetNumber() string {
if !strings.HasSuffix(setNumber, "-1") {
return setNumber + "-1"
}
return setNumber
}Problem: The "-1" suffix requirement is unexplained. A comment would help explain this is the Rebrickable set variant convention.
Commands like saveSetsCmd don't validate that required flags are provided before making API calls.
| Priority | Issue | Location | Status |
|---|---|---|---|
user.go:60-63 |
✅ Fixed | ||
api.go |
✅ Fixed | ||
| High | Unsafe type assertions | sets.go (8 places) |
❌ Open |
api.go:100-123 |
✅ Fixed | ||
| ✅ Fixed | |||
| Medium | Untyped context keys | user.go:17-20 |
❌ Open |
| Low | Global flag variables | sets.go:12-13 |
❌ Open |
api.go |
✅ Fixed |
- Clean separation between
cmdandapipackages - Proper use of Cobra's
PersistentPreRunEfor authentication - Credentials kept in environment variables (not hardcoded)
- Bazel build setup with proper dependency management
- Integration tests using testscript framework
- Token masking in debug output (
api.go:89)
To refresh this document after making code changes:
check CODE_REVIEW.md, review the code and update the doc