Skip to content

Commit 268ed5e

Browse files
committed
INF-1307 zero out Go Report Card warnings + add badge
Ran every tool Go Report Card runs (gofmt, go vet, ineffassign, misspell, gocyclo over 15, golint) and got each to clean. Specific cleanup: - Refactored buildOperationCommand (cyclomatic complexity 57 -> < 15) by splitting into bindParameters, bindBodyFlags, runOperation, buildEndpoint, executeRequest helpers - Refactored runCatalog (20) and printDescribe (16) the same way - Removed dead `_ = typ` line from parameter binding - Removed unused pathItem field from the opEntry struct - Added missing doc comments on exported symbols in internal/config (SandboxBaseURL, EnvSandbox, BaseURL) and internal/updater (Asset, PromptResponse iota) - Dropped the duplicate package-level comment on operations.go scripts/lint.sh runs the same six tools locally; ci.yml gains a lint job that installs them and runs the script, so regressions surface before goreportcard.com sees them. Badges (CI, Go Report Card, MIT) added to the README header.
1 parent aff8c9c commit 268ed5e

8 files changed

Lines changed: 511 additions & 350 deletions

File tree

.github/workflows/ci.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,21 @@ jobs:
1919
go-version: '1.26'
2020
cache: true
2121
- run: go build ./...
22-
- run: go vet ./...
2322
- run: go test ./...
23+
24+
lint:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v5
28+
- uses: actions/setup-go@v6
29+
with:
30+
go-version: '1.26'
31+
cache: true
32+
- name: Install lint tools
33+
run: |
34+
go install github.com/gordonklaus/ineffassign@latest
35+
go install github.com/client9/misspell/cmd/misspell@latest
36+
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
37+
go install golang.org/x/lint/golint@latest
38+
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
39+
- run: ./scripts/lint.sh

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# twoctl
22

3+
[![ci](https://github.com/two-inc/twoctl/actions/workflows/ci.yml/badge.svg)](https://github.com/two-inc/twoctl/actions/workflows/ci.yml)
4+
[![Go Report Card](https://goreportcard.com/badge/github.com/two-inc/twoctl)](https://goreportcard.com/report/github.com/two-inc/twoctl)
5+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6+
37
Command-line interface for the [Two](https://two.inc) merchant APIs. Generated from the [public OpenAPI specs](https://github.com/two-inc/docs/tree/main/openapi), so every endpoint is exposed as a subcommand.
48

59
## Install

cmd/twoctl/cli/catalog.go

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ type catalogFlag struct {
3333
}
3434

3535
type catalogOp struct {
36-
Command string `json:"command"` // e.g. "twoctl checkout create-order"
37-
API string `json:"api"` // "checkout"
36+
Command string `json:"command"` // e.g. "twoctl checkout create-order"
37+
API string `json:"api"` // "checkout"
3838
OperationID string `json:"operation_id,omitempty"`
3939
Method string `json:"method"`
4040
Path string `json:"path"`
@@ -46,69 +46,83 @@ type catalogOp struct {
4646

4747
func runCatalog(cmd *cobra.Command, args []string) error {
4848
wantAPI, _ := cmd.Flags().GetString("api")
49-
var out []catalogOp
49+
out, err := collectCatalog(wantAPI)
50+
if err != nil {
51+
return err
52+
}
53+
sort.Slice(out, func(i, j int) bool { return out[i].Command < out[j].Command })
54+
enc := json.NewEncoder(cmd.OutOrStdout())
55+
enc.SetIndent("", " ")
56+
return enc.Encode(out)
57+
}
5058

59+
func collectCatalog(wantAPI string) ([]catalogOp, error) {
60+
var out []catalogOp
5161
for _, m := range apiMeta {
5262
if wantAPI != "" && wantAPI != m.use {
5363
continue
5464
}
55-
raw, err := specFS.ReadFile("specs/" + m.file)
65+
doc, err := loadEmbeddedSpec(m.file)
5666
if err != nil {
57-
return err
67+
return nil, err
5868
}
59-
doc, err := openapi3.NewLoader().LoadFromData(raw)
60-
if err != nil {
61-
return err
69+
for _, e := range walkOperations(doc) {
70+
out = append(out, catalogEntryFor(m.use, e))
6271
}
63-
for path, item := range doc.Paths.Map() {
64-
for method, op := range item.Operations() {
65-
if op == nil {
66-
continue
67-
}
68-
name := commandNameFor(op, method, path)
69-
for _, s := range stripSuffixes {
70-
if len(name) > len(s) && name[len(name)-len(s):] == s {
71-
name = name[:len(name)-len(s)]
72-
break
73-
}
74-
}
75-
entry := catalogOp{
76-
Command: "twoctl " + m.use + " " + name,
77-
API: m.use,
78-
OperationID: op.OperationID,
79-
Method: method,
80-
Path: path,
81-
Summary: op.Summary,
82-
}
83-
for _, ref := range op.Parameters {
84-
p := ref.Value
85-
if p == nil {
86-
continue
87-
}
88-
t := "string"
89-
if p.Schema != nil && p.Schema.Value != nil && p.Schema.Value.Type != nil {
90-
if s := p.Schema.Value.Type.Slice(); len(s) > 0 {
91-
t = s[0]
92-
}
93-
}
94-
entry.Flags = append(entry.Flags, catalogFlag{
95-
Name: toKebab(p.Name),
96-
In: p.In,
97-
Type: t,
98-
Required: p.Required,
99-
Desc: p.Description,
100-
})
101-
}
102-
if op.RequestBody != nil && op.RequestBody.Value != nil {
103-
entry.HasBody = true
104-
entry.BodyRequired = op.RequestBody.Value.Required
105-
}
106-
out = append(out, entry)
107-
}
72+
}
73+
return out, nil
74+
}
75+
76+
func loadEmbeddedSpec(file string) (*openapi3.T, error) {
77+
raw, err := specFS.ReadFile("specs/" + file)
78+
if err != nil {
79+
return nil, err
80+
}
81+
return openapi3.NewLoader().LoadFromData(raw)
82+
}
83+
84+
func catalogEntryFor(api string, e opEntry) catalogOp {
85+
entry := catalogOp{
86+
Command: "twoctl " + api + " " + e.name,
87+
API: api,
88+
OperationID: e.op.OperationID,
89+
Method: e.method,
90+
Path: e.path,
91+
Summary: e.op.Summary,
92+
Flags: catalogFlagsFor(e.op),
93+
}
94+
if e.op.RequestBody != nil && e.op.RequestBody.Value != nil {
95+
entry.HasBody = true
96+
entry.BodyRequired = e.op.RequestBody.Value.Required
97+
}
98+
return entry
99+
}
100+
101+
func catalogFlagsFor(op *openapi3.Operation) []catalogFlag {
102+
var flags []catalogFlag
103+
for _, ref := range op.Parameters {
104+
p := ref.Value
105+
if p == nil {
106+
continue
108107
}
108+
flags = append(flags, catalogFlag{
109+
Name: toKebab(p.Name),
110+
In: p.In,
111+
Type: flagTypeOf(p),
112+
Required: p.Required,
113+
Desc: p.Description,
114+
})
109115
}
110-
sort.Slice(out, func(i, j int) bool { return out[i].Command < out[j].Command })
111-
enc := json.NewEncoder(cmd.OutOrStdout())
112-
enc.SetIndent("", " ")
113-
return enc.Encode(out)
116+
return flags
117+
}
118+
119+
func flagTypeOf(p *openapi3.Parameter) string {
120+
sch := schemaOf(p)
121+
if sch == nil || sch.Type == nil {
122+
return "string"
123+
}
124+
if s := sch.Type.Slice(); len(s) > 0 {
125+
return s[0]
126+
}
127+
return "string"
114128
}

0 commit comments

Comments
 (0)