← back to Cli Printing Press
fix(cli): route live cache through typed upserts (#751)
c332b534e59ac6e4d786c48924be81457c85f1a1 · 2026-05-08 15:26:49 -0700 · Trevin Chow
Files touched
M internal/generator/generator_test.goM internal/generator/templates/data_source.go.tmpl
Diff
commit c332b534e59ac6e4d786c48924be81457c85f1a1
Author: Trevin Chow <trevin@trevinchow.com>
Date: Fri May 8 15:26:49 2026 -0700
fix(cli): route live cache through typed upserts (#751)
---
internal/generator/generator_test.go | 72 ++++++++++++++++++++++--
internal/generator/templates/data_source.go.tmpl | 17 ++----
2 files changed, 72 insertions(+), 17 deletions(-)
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index a2723862..b5c4d6a0 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -2364,10 +2364,8 @@ func TestExtractPageItemsNoCursor(t *testing.T) {
runGoCommandRequired(t, outputDir, "test", "-run", "TestExtractPageItems", "./internal/cli")
}
-func TestGenerateStoreUpsertBatchDispatchesToTypedTable(t *testing.T) {
- t.Parallel()
-
- apiSpec := &spec.APISpec{
+func adsCampaignSpec() *spec.APISpec {
+ return &spec.APISpec{
Name: "ads",
Version: "0.1.0",
BaseURL: "https://api.example.com",
@@ -2403,7 +2401,12 @@ func TestGenerateStoreUpsertBatchDispatchesToTypedTable(t *testing.T) {
},
},
}
+}
+func TestGenerateStoreUpsertBatchDispatchesToTypedTable(t *testing.T) {
+ t.Parallel()
+
+ apiSpec := adsCampaignSpec()
outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
gen := New(apiSpec, outputDir)
gen.VisionSet = VisionTemplateSet{Store: true}
@@ -2426,6 +2429,67 @@ func TestGenerateStoreUpsertBatchDispatchesToTypedTable(t *testing.T) {
runGoCommand(t, outputDir, "test", "./internal/store")
}
+func TestLiveFetchWriteThroughCachePopulatesTypedTable(t *testing.T) {
+ t.Parallel()
+
+ apiSpec := adsCampaignSpec()
+ outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+ gen := New(apiSpec, outputDir)
+ gen.VisionSet = VisionTemplateSet{Store: true}
+ require.NoError(t, gen.Generate())
+
+ inlineTest := `package cli
+
+import (
+ "context"
+ "encoding/json"
+ "testing"
+
+ "` + naming.CLI(apiSpec.Name) + `/internal/store"
+)
+
+func TestWriteThroughCachePopulatesTypedTable(t *testing.T) {
+ t.Setenv("HOME", t.TempDir())
+
+ writeThroughCache(context.Background(), "campaigns", json.RawMessage(` + "`" + `[
+ {"id":"camp_1","name":"Launch","status":"active","account_id":"acct_1"}
+ ]` + "`" + `))
+
+ db, err := store.Open(defaultDBPath("ads-pp-cli"))
+ if err != nil {
+ t.Fatalf("open cache store: %v", err)
+ }
+ defer db.Close()
+
+ var typedCount int
+ var accountID string
+ if err := db.DB().QueryRow(` + "`" + `SELECT COUNT(*), COALESCE(MAX(account_id), '') FROM campaigns` + "`" + `).Scan(&typedCount, &accountID); err != nil {
+ t.Fatalf("query campaigns: %v", err)
+ }
+ if typedCount != 1 || accountID != "acct_1" {
+ t.Fatalf("typed campaigns = %d/%q, want 1/acct_1", typedCount, accountID)
+ }
+
+ writeThroughCache(context.Background(), "events", json.RawMessage(` + "`" + `[
+ {"id":"evt_1","name":"Seen"}
+ ]` + "`" + `))
+
+ var genericCount int
+ if err := db.DB().QueryRow(` + "`" + `SELECT COUNT(*) FROM resources WHERE resource_type = 'events'` + "`" + `).Scan(&genericCount); err != nil {
+ t.Fatalf("query generic resources: %v", err)
+ }
+ if genericCount != 1 {
+ t.Fatalf("generic events count = %d, want 1", genericCount)
+ }
+}
+`
+ testPath := filepath.Join(outputDir, "internal", "cli", "write_through_cache_test.go")
+ require.NoError(t, os.WriteFile(testPath, []byte(inlineTest), 0o644))
+
+ runGoCommandRequired(t, outputDir, "mod", "tidy")
+ runGoCommandRequired(t, outputDir, "test", "-run", "TestWriteThroughCachePopulatesTypedTable", "./internal/cli")
+}
+
func TestSyncDiscriminatorDispatchRoutesMixedItemsToTypedTables(t *testing.T) {
t.Parallel()
diff --git a/internal/generator/templates/data_source.go.tmpl b/internal/generator/templates/data_source.go.tmpl
index 234f7cbe..b2397b58 100644
--- a/internal/generator/templates/data_source.go.tmpl
+++ b/internal/generator/templates/data_source.go.tmpl
@@ -187,25 +187,16 @@ func writeThroughCache(ctx context.Context, resourceType string, data json.RawMe
}
// Single object with an id field (e.g., detail response)
if items == nil {
- if idRaw, ok := envelope["id"]; ok {
- id := strings.Trim(string(idRaw), "\"")
- _ = db.Upsert(resourceType, id, data)
+ if _, ok := envelope["id"]; ok {
+ _, _, _ = db.UpsertBatch(resourceType, []json.RawMessage{data})
return
}
}
}
}
- // Upsert each item individually
- for _, item := range items {
- var obj map[string]json.RawMessage
- if json.Unmarshal(item, &obj) != nil {
- continue
- }
- if idRaw, ok := obj["id"]; ok {
- id := strings.Trim(string(idRaw), "\"")
- _ = db.Upsert(resourceType, id, item)
- }
+ if len(items) > 0 {
+ _, _, _ = db.UpsertBatch(resourceType, items)
}
}
← c397021f fix(cli): harden force-generate preservation (#750)
·
back to Cli Printing Press
·
Add issue ownership guidance to AGENTS.md (#754) 7990021b →