← back to Cli Printing Press
fix(generator): add PATCH support + skip unexported fields in types
588c694e908b978f5446f618ae60748c2bb53e6c · 2026-03-23 17:12:51 -0700 · Matt Van Horn
Add PATCH case to command template and Patch method to client template.
Skip schema properties starting with _ in type generation since they
produce unexported Go fields with JSON tags that fail go vet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Files touched
M internal/generator/templates/client.go.tmplM internal/generator/templates/command.go.tmplM internal/openapi/parser.goM internal/openapi/parser_test.go
Diff
commit 588c694e908b978f5446f618ae60748c2bb53e6c
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date: Mon Mar 23 17:12:51 2026 -0700
fix(generator): add PATCH support + skip unexported fields in types
Add PATCH case to command template and Patch method to client template.
Skip schema properties starting with _ in type generation since they
produce unexported Go fields with JSON tags that fail go vet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
internal/generator/templates/client.go.tmpl | 4 +++
internal/generator/templates/command.go.tmpl | 8 +++++
internal/openapi/parser.go | 6 ++++
internal/openapi/parser_test.go | 46 ++++++++++++++++++++++++++++
4 files changed, 64 insertions(+)
diff --git a/internal/generator/templates/client.go.tmpl b/internal/generator/templates/client.go.tmpl
index 5a2391f7..8c1f921c 100644
--- a/internal/generator/templates/client.go.tmpl
+++ b/internal/generator/templates/client.go.tmpl
@@ -39,6 +39,10 @@ func (c *Client) Put(path string, body any) (json.RawMessage, error) {
return c.do("PUT", path, nil, body)
}
+func (c *Client) Patch(path string, body any) (json.RawMessage, error) {
+ return c.do("PATCH", path, nil, body)
+}
+
func (c *Client) do(method, path string, params map[string]string, body any) (json.RawMessage, error) {
url := c.BaseURL + path
diff --git a/internal/generator/templates/command.go.tmpl b/internal/generator/templates/command.go.tmpl
index 422c1a42..b4ef5270 100644
--- a/internal/generator/templates/command.go.tmpl
+++ b/internal/generator/templates/command.go.tmpl
@@ -78,6 +78,14 @@ func new{{title $.ResourceName}}{{title $eName}}Cmd(flags *rootFlags) *cobra.Com
}
{{- end}}
data, err := c.Put(path, body)
+{{- else if eq $endpoint.Method "PATCH"}}
+ body := map[string]any{}
+{{- range $endpoint.Body}}
+ if body{{camel .Name}} != {{zeroVal .Type}} {
+ body["{{.Name}}"] = body{{camel .Name}}
+ }
+{{- end}}
+ data, err := c.Patch(path, body)
{{- end}}
if err != nil {
return apiErr(err)
diff --git a/internal/openapi/parser.go b/internal/openapi/parser.go
index 4883c550..e7bcee9a 100644
--- a/internal/openapi/parser.go
+++ b/internal/openapi/parser.go
@@ -643,6 +643,9 @@ func mapTypes(doc *openapi3.T, out *spec.APISpec) {
fields := make([]spec.TypeField, 0, len(fieldNames))
for _, fieldName := range fieldNames {
+ if strings.HasPrefix(fieldName, "_") {
+ continue
+ }
fields = append(fields, spec.TypeField{
Name: fieldName,
Type: mapSchemaType(schemaRefValue(properties[fieldName])),
@@ -668,6 +671,9 @@ func collectTypeProperties(schemaRef *openapi3.SchemaRef, properties map[string]
if prop == nil {
continue
}
+ if strings.HasPrefix(name, "_") {
+ continue
+ }
properties[name] = prop
}
for _, sub := range schema.AllOf {
diff --git a/internal/openapi/parser_test.go b/internal/openapi/parser_test.go
index c5cef31b..18b10300 100644
--- a/internal/openapi/parser_test.go
+++ b/internal/openapi/parser_test.go
@@ -58,6 +58,52 @@ func TestParseStytchOpenAPI(t *testing.T) {
assert.Greater(t, totalEndpoints, 10)
}
+func TestSkipUnderscoreFields(t *testing.T) {
+ spec := []byte(`
+openapi: "3.0.0"
+info:
+ title: Test
+ version: "1.0"
+servers:
+ - url: https://api.example.com
+paths:
+ /items:
+ get:
+ operationId: listItems
+ responses:
+ "200":
+ description: OK
+components:
+ schemas:
+ Item:
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ _errors:
+ type: object
+ _internal:
+ type: string
+`)
+ parsed, err := Parse(spec)
+ require.NoError(t, err)
+
+ item, ok := parsed.Types["Item"]
+ require.True(t, ok)
+
+ // Should have id and name but NOT _errors or _internal
+ fieldNames := make([]string, 0)
+ for _, f := range item.Fields {
+ fieldNames = append(fieldNames, f.Name)
+ }
+ assert.Contains(t, fieldNames, "id")
+ assert.Contains(t, fieldNames, "name")
+ assert.NotContains(t, fieldNames, "_errors")
+ assert.NotContains(t, fieldNames, "_internal")
+}
+
func TestIsOpenAPI(t *testing.T) {
t.Parallel()
← d3393f88 docs: add Phase 4 plan
·
back to Cli Printing Press
·
fix(openapi): smart operationId cleaning for clean command n 84823431 →