← back to Cli Printing Press
fix(cli): avoid duplicate batch store upsert generation (#231)
24785ee5bc564a1c7e5d5cab82385b114719a55d · 2026-04-24 17:13:33 -0700 · Pejman Pour-Moezzi
Files touched
M internal/generator/generator.goM internal/generator/generator_test.goM internal/generator/templates/store.go.tmplM internal/generator/templates/sync.go.tmpl
Diff
commit 24785ee5bc564a1c7e5d5cab82385b114719a55d
Author: Pejman Pour-Moezzi <pejman.pourmoezzi@gmail.com>
Date: Fri Apr 24 17:13:33 2026 -0700
fix(cli): avoid duplicate batch store upsert generation (#231)
---
internal/generator/generator.go | 7 ++++
internal/generator/generator_test.go | 54 ++++++++++++++++++++++++++++++
internal/generator/templates/store.go.tmpl | 2 +-
internal/generator/templates/sync.go.tmpl | 2 +-
4 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index 328a493a..b7a40c0e 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -249,6 +249,9 @@ func New(s *spec.APISpec, outputDir string) *Generator {
},
"envName": func(s string) string { return strings.ToUpper(strings.ReplaceAll(s, "-", "_")) },
"safeName": safeSQLName,
+ "hasDomainUpsert": func(name string) bool {
+ return domainUpsertMethodName(name) != "UpsertBatch"
+ },
"pathContainsParam": func(path, name string) bool {
return strings.Contains(path, "{"+name+"}")
},
@@ -1737,6 +1740,10 @@ func toPascal(s string) string {
return strings.Join(parts, "")
}
+func domainUpsertMethodName(tableName string) string {
+ return "Upsert" + toPascal(tableName)
+}
+
// isIDParam returns true if the parameter name suggests it's an identifier
// that should be typed as string regardless of the spec's declared type.
// IDs like steamid (17-digit number) overflow int64, and zero-value confusion
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index 10b63b33..6607dd8d 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -872,6 +872,60 @@ func TestGenerateWithEmptyOwner(t *testing.T) {
assert.NotContains(t, string(gomod), "module github.com/")
}
+func TestGenerateStoreWithBatchResourceDoesNotDuplicateUpsertBatch(t *testing.T) {
+ t.Parallel()
+
+ apiSpec := &spec.APISpec{
+ Name: "batch",
+ Version: "0.1.0",
+ BaseURL: "https://api.example.com",
+ Auth: spec.AuthConfig{Type: "none"},
+ Config: spec.ConfigSpec{
+ Format: "toml",
+ Path: "~/.config/batch-pp-cli/config.toml",
+ },
+ Resources: map[string]spec.Resource{
+ "batch": {
+ Description: "Manage batch jobs",
+ Endpoints: map[string]spec.Endpoint{
+ "list": {
+ Method: "GET",
+ Path: "/batch",
+ Description: "List batch jobs",
+ Params: []spec.Param{
+ {Name: "id", Type: "string"},
+ {Name: "name", Type: "string"},
+ {Name: "status", Type: "string"},
+ {Name: "created_at", Type: "string", Format: "date-time"},
+ },
+ },
+ "create": {
+ Method: "POST",
+ Path: "/batch",
+ Description: "Create a batch job",
+ Body: []spec.Param{
+ {Name: "name", Type: "string"},
+ {Name: "description", Type: "string"},
+ },
+ },
+ },
+ },
+ },
+ }
+
+ outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+ gen := New(apiSpec, outputDir)
+ gen.VisionSet = VisionTemplateSet{Store: true}
+ require.NoError(t, gen.Generate())
+
+ storeSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "store", "store.go"))
+ require.NoError(t, err)
+ assert.Equal(t, 1, strings.Count(string(storeSrc), "func (s *Store) UpsertBatch("))
+
+ runGoCommand(t, outputDir, "mod", "tidy")
+ runGoCommand(t, outputDir, "test", "./internal/store")
+}
+
// --- Unit 7: Feature Verification Tests ---
func generatePetstore(t *testing.T) string {
diff --git a/internal/generator/templates/store.go.tmpl b/internal/generator/templates/store.go.tmpl
index 14b66792..5db059e2 100644
--- a/internal/generator/templates/store.go.tmpl
+++ b/internal/generator/templates/store.go.tmpl
@@ -344,7 +344,7 @@ func lookupFieldValue(obj map[string]any, snakeKey string) any {
}
{{- range .Tables}}
-{{- if and (gt (len .Columns) 3) (ne .Name "sync_state")}}
+{{- if and (gt (len .Columns) 3) (ne .Name "sync_state") (hasDomainUpsert .Name)}}
// Upsert{{pascal .Name}} inserts or updates a {{.Name}} record with domain-specific columns.
func (s *Store) Upsert{{pascal .Name}}(data json.RawMessage) error {
var obj map[string]any
diff --git a/internal/generator/templates/sync.go.tmpl b/internal/generator/templates/sync.go.tmpl
index 87a074ec..aa843495 100644
--- a/internal/generator/templates/sync.go.tmpl
+++ b/internal/generator/templates/sync.go.tmpl
@@ -499,7 +499,7 @@ func upsertSingleObject(db *store.Store, resource string, data json.RawMessage)
switch resource {
{{- range .Tables}}
-{{- if and (gt (len .Columns) 3) (ne .Name "sync_state")}}
+{{- if and (gt (len .Columns) 3) (ne .Name "sync_state") (hasDomainUpsert .Name)}}
case "{{.Name}}":
return db.Upsert{{pascal .Name}}(data)
{{- end}}
← 55b3f5e1 fix(megamcp): reject env var values containing '}' in ApplyA
·
back to Cli Printing Press
·
fix(cli): stop doctor from claiming valid creds are invalid 6a184771 →