← back to Cli Printing Press
feat(cli): support static config headers (#769)
838f5a497975aad11e7c938f982f7a64d96d9a15 · 2026-05-08 19:38:47 -0700 · Trevin Chow
Files touched
A internal/generator/config_headers_test.goM internal/generator/templates/client.go.tmplM internal/generator/templates/config.go.tmplM internal/generator/templates/readme.md.tmplM testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.goM testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/config/config.goM testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.mdM testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/config/config.goM testdata/golden/expected/generate-golden-api/printing-press-golden/README.mdM testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.goM testdata/golden/expected/generate-golden-api/printing-press-golden/internal/config/config.goM testdata/golden/expected/generate-public-param-names/public-param-golden/README.mdM testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
Diff
commit 838f5a497975aad11e7c938f982f7a64d96d9a15
Author: Trevin Chow <trevin@trevinchow.com>
Date: Fri May 8 19:38:47 2026 -0700
feat(cli): support static config headers (#769)
---
internal/generator/config_headers_test.go | 70 ++++++++++++++++++++++
internal/generator/templates/client.go.tmpl | 5 ++
internal/generator/templates/config.go.tmpl | 1 +
internal/generator/templates/readme.md.tmpl | 2 +
.../internal/client/client.go | 5 ++
.../internal/config/config.go | 1 +
.../printing-press-rich-auth/README.md | 2 +
.../internal/config/config.go | 1 +
.../printing-press-golden/README.md | 2 +
.../internal/client/client.go | 5 ++
.../internal/config/config.go | 1 +
.../public-param-golden/README.md | 2 +
.../tier-routing-golden/internal/client/client.go | 5 ++
13 files changed, 102 insertions(+)
diff --git a/internal/generator/config_headers_test.go b/internal/generator/config_headers_test.go
new file mode 100644
index 00000000..a8a8fc71
--- /dev/null
+++ b/internal/generator/config_headers_test.go
@@ -0,0 +1,70 @@
+package generator
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/mvanhorn/cli-printing-press/v4/internal/naming"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGeneratedClientAppliesConfigHeadersBeforeRequestOverrides(t *testing.T) {
+ t.Parallel()
+
+ apiSpec := minimalSpec("static-headers")
+ outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+ require.NoError(t, New(apiSpec, outputDir).Generate())
+
+ const behaviorTest = `package client
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ "static-headers-pp-cli/internal/config"
+)
+
+func TestConfigHeadersAreAppliedBeforeRequestOverrides(t *testing.T) {
+ var seen int
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ seen++
+ if got := r.Header.Get("Authorization"); got != "Bearer config-token" {
+ t.Fatalf("Authorization header = %q, want %q", got, "Bearer config-token")
+ }
+ if got := r.Header.Get("apikey"); got != "config-key" {
+ t.Fatalf("apikey header = %q, want %q", got, "config-key")
+ }
+ if got := r.Header.Get("x-api-version"); got != "override-version" {
+ t.Fatalf("x-api-version header = %q, want request override to win", got)
+ }
+ w.Header().Set("Content-Type", "application/json")
+ _, _ = w.Write([]byte(` + "`" + `{"ok":true}` + "`" + `))
+ }))
+ defer server.Close()
+
+ cfg := &config.Config{
+ BaseURL: server.URL,
+ AuthHeaderVal: "Bearer config-token",
+ Headers: map[string]string{
+ "apikey": "config-key",
+ "x-api-version": "config-version",
+ },
+ }
+ c := New(cfg, time.Second, 0)
+ c.NoCache = true
+
+ if _, err := c.GetWithHeaders("/items", nil, map[string]string{"x-api-version": "override-version"}); err != nil {
+ t.Fatalf("GetWithHeaders returned error: %v", err)
+ }
+ if seen != 1 {
+ t.Fatalf("server saw %d requests, want 1", seen)
+ }
+}
+`
+ require.NoError(t, os.WriteFile(filepath.Join(outputDir, "internal", "client", "config_headers_test.go"), []byte(behaviorTest), 0o644))
+
+ runGoCommand(t, outputDir, "test", "./internal/client")
+}
diff --git a/internal/generator/templates/client.go.tmpl b/internal/generator/templates/client.go.tmpl
index dc2fd59b..0140677c 100644
--- a/internal/generator/templates/client.go.tmpl
+++ b/internal/generator/templates/client.go.tmpl
@@ -670,6 +670,11 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
{{- range .RequiredHeaders}}
req.Header.Set("{{.Name}}", "{{.Value}}")
{{- end}}
+ if c.Config != nil {
+ for k, v := range c.Config.Headers {
+ req.Header.Set(k, v)
+ }
+ }
// Per-endpoint header overrides (e.g., different API version per resource)
for k, v := range headerOverrides {
req.Header.Set(k, v)
diff --git a/internal/generator/templates/config.go.tmpl b/internal/generator/templates/config.go.tmpl
index 1e72b242..ccff6502 100644
--- a/internal/generator/templates/config.go.tmpl
+++ b/internal/generator/templates/config.go.tmpl
@@ -23,6 +23,7 @@ import (
type Config struct {
BaseURL string `{{configTag .Config.Format}}:"base_url"`
AuthHeaderVal string `{{configTag .Config.Format}}:"auth_header"`
+ Headers map[string]string `{{configTag .Config.Format}}:"headers,omitempty"`
AuthSource string `{{configTag .Config.Format}}:"-"`
{{- if .HasAuthCommand}}
AccessToken string `{{configTag .Config.Format}}:"access_token"`
diff --git a/internal/generator/templates/readme.md.tmpl b/internal/generator/templates/readme.md.tmpl
index f8f1f62e..fdfbf688 100644
--- a/internal/generator/templates/readme.md.tmpl
+++ b/internal/generator/templates/readme.md.tmpl
@@ -490,6 +490,8 @@ Verifies configuration{{if and (ne .Auth.Type "") (ne .Auth.Type "none")}}, cred
## Configuration
Config file: `{{.Config.Path}}`
+
+Static request headers can be configured under `headers`; per-command header overrides take precedence.
{{- if or .Auth.EnvVarSpecs .Auth.EnvVars}}
Environment variables:
diff --git a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
index 6966ce01..901bd1fc 100644
--- a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
+++ b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
@@ -227,6 +227,11 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
if authHeader != "" {
req.Header.Set("Authorization", authHeader)
}
+ if c.Config != nil {
+ for k, v := range c.Config.Headers {
+ req.Header.Set(k, v)
+ }
+ }
// Per-endpoint header overrides (e.g., different API version per resource)
for k, v := range headerOverrides {
req.Header.Set(k, v)
diff --git a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/config/config.go b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/config/config.go
index d5bac5f0..9d50f491 100644
--- a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/config/config.go
+++ b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/config/config.go
@@ -16,6 +16,7 @@ import (
type Config struct {
BaseURL string `toml:"base_url"`
AuthHeaderVal string `toml:"auth_header"`
+ Headers map[string]string `toml:"headers,omitempty"`
AuthSource string `toml:"-"`
AccessToken string `toml:"access_token"`
RefreshToken string `toml:"refresh_token"`
diff --git a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.md b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.md
index 6ef17e40..0ddca815 100644
--- a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.md
+++ b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.md
@@ -201,6 +201,8 @@ Verifies configuration, credentials, and connectivity to the API.
Config file: `~/.config/printing-press-rich-pp-cli/config.toml`
+Static request headers can be configured under `headers`; per-command header overrides take precedence.
+
Environment variables:
| Name | Kind | Required | Description |
diff --git a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/config/config.go b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/config/config.go
index ec271074..33fb8aaa 100644
--- a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/config/config.go
+++ b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/config/config.go
@@ -16,6 +16,7 @@ import (
type Config struct {
BaseURL string `toml:"base_url"`
AuthHeaderVal string `toml:"auth_header"`
+ Headers map[string]string `toml:"headers,omitempty"`
AuthSource string `toml:"-"`
AccessToken string `toml:"access_token"`
RefreshToken string `toml:"refresh_token"`
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/README.md b/testdata/golden/expected/generate-golden-api/printing-press-golden/README.md
index 5c5d2154..982dea47 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/README.md
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/README.md
@@ -222,6 +222,8 @@ Verifies configuration, credentials, and connectivity to the API.
Config file: `~/.config/printing-press-golden-pp-cli/config.toml`
+Static request headers can be configured under `headers`; per-command header overrides take precedence.
+
Environment variables:
| Name | Kind | Required | Description |
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
index 52c5cced..80eff7fb 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
@@ -222,6 +222,11 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
req.Header.Set("X-API-Key", authHeader)
}
req.Header.Set("X-Api-Version", "2026-04-01")
+ if c.Config != nil {
+ for k, v := range c.Config.Headers {
+ req.Header.Set(k, v)
+ }
+ }
// Per-endpoint header overrides (e.g., different API version per resource)
for k, v := range headerOverrides {
req.Header.Set(k, v)
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/config/config.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/config/config.go
index a4d51dcb..cb12c836 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/config/config.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/config/config.go
@@ -16,6 +16,7 @@ import (
type Config struct {
BaseURL string `toml:"base_url"`
AuthHeaderVal string `toml:"auth_header"`
+ Headers map[string]string `toml:"headers,omitempty"`
AuthSource string `toml:"-"`
AccessToken string `toml:"access_token"`
RefreshToken string `toml:"refresh_token"`
diff --git a/testdata/golden/expected/generate-public-param-names/public-param-golden/README.md b/testdata/golden/expected/generate-public-param-names/public-param-golden/README.md
index 2a6280ec..84ff2e89 100644
--- a/testdata/golden/expected/generate-public-param-names/public-param-golden/README.md
+++ b/testdata/golden/expected/generate-public-param-names/public-param-golden/README.md
@@ -189,6 +189,8 @@ Verifies configuration and connectivity to the API.
Config file: ``
+Static request headers can be configured under `headers`; per-command header overrides take precedence.
+
## Troubleshooting
**Not found errors (exit code 3)**
- Check the resource ID is correct
diff --git a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
index 4f13cb33..09787659 100644
--- a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
+++ b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
@@ -325,6 +325,11 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
req.Header.Set(authInfo.Name, authHeader)
}
}
+ if c.Config != nil {
+ for k, v := range c.Config.Headers {
+ req.Header.Set(k, v)
+ }
+ }
// Per-endpoint header overrides (e.g., different API version per resource)
for k, v := range headerOverrides {
req.Header.Set(k, v)
← a5b57248 chore(main): release 4.1.0 (#729)
·
back to Cli Printing Press
·
fix(cli): avoid duplicate token auth map keys (#775) de34ec0e →