← back to Cli Printing Press
feat(cli): substitute EndpointTemplateVars in client.do() + cache key
8cb57db37db3806997af5520ed79f6d4bc37daf3 · 2026-04-29 21:48:47 -0500 · Cathryn Lavery
Route every request through buildURL(c.BaseURL, path, c.Config.TemplateVars)
when the spec declares EndpointTemplateVars, replacing the static
c.BaseURL+path concat. Both BaseURL ("https://{shop}") and path
("/admin/api/{api_version}/graphql.json") may carry placeholders, and a
single helper at do()'s entry covers GET/POST/PUT/PATCH/DELETE plus the
proxy-envelope branch.
Mix the resolved template-var values into cacheKey so two tenants on the
same logical path land in different cache entries — without this, a warm
SHOPIFY_SHOP=A cache would feed responses to a SHOPIFY_SHOP=B caller.
Flipping a value back to unset also misses the warm cache and surfaces the
TemplateVarError from buildURL instead of silently returning stale data.
Specs without EndpointTemplateVars keep the original concat; client.go
output is byte-identical for Linear, REST, and every existing fixture.
Files touched
M internal/generator/templates/client.go.tmpl
Diff
commit 8cb57db37db3806997af5520ed79f6d4bc37daf3
Author: Cathryn Lavery <50469282+cathrynlavery@users.noreply.github.com>
Date: Wed Apr 29 21:48:47 2026 -0500
feat(cli): substitute EndpointTemplateVars in client.do() + cache key
Route every request through buildURL(c.BaseURL, path, c.Config.TemplateVars)
when the spec declares EndpointTemplateVars, replacing the static
c.BaseURL+path concat. Both BaseURL ("https://{shop}") and path
("/admin/api/{api_version}/graphql.json") may carry placeholders, and a
single helper at do()'s entry covers GET/POST/PUT/PATCH/DELETE plus the
proxy-envelope branch.
Mix the resolved template-var values into cacheKey so two tenants on the
same logical path land in different cache entries — without this, a warm
SHOPIFY_SHOP=A cache would feed responses to a SHOPIFY_SHOP=B caller.
Flipping a value back to unset also misses the warm cache and surfaces the
TemplateVarError from buildURL instead of silently returning stale data.
Specs without EndpointTemplateVars keep the original concat; client.go
output is byte-identical for Linear, REST, and every existing fixture.
---
internal/generator/templates/client.go.tmpl | 40 +++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/internal/generator/templates/client.go.tmpl b/internal/generator/templates/client.go.tmpl
index 65441183..f2042fe0 100644
--- a/internal/generator/templates/client.go.tmpl
+++ b/internal/generator/templates/client.go.tmpl
@@ -185,6 +185,21 @@ func (c *Client) cacheKey(path string, params map[string]string) string {
for k, v := range params {
key += k + "=" + v
}
+{{- if .EndpointTemplateVars}}
+ // Include resolved template-var values in the cache identity so two
+ // tenants (different SHOPIFY_SHOP) never collide on the same path, and
+ // flipping a value back to unset misses the warm cache and surfaces
+ // the actionable error from buildURL instead of returning stale data.
+ if c.Config != nil {
+ for _, name := range []string{
+ {{- range .EndpointTemplateVars}}
+ "{{.}}",
+ {{- end}}
+ } {
+ key += "|" + name + "=" + c.Config.TemplateVars[name]
+ }
+ }
+{{- end}}
h := sha256.Sum256([]byte(key))
return hex.EncodeToString(h[:8])
}
@@ -243,11 +258,36 @@ func (c *Client) PatchWithHeaders(path string, body any, headers map[string]stri
// do executes an HTTP request. headerOverrides, when non-nil, override global
// RequiredHeaders for this specific request (used for per-endpoint API versioning).
func (c *Client) do(method, path string, params map[string]string, body any, headerOverrides map[string]string) (json.RawMessage, int, error) {
+{{- if .EndpointTemplateVars}}
+ // EndpointTemplateVars are declared in the spec — resolve {placeholder}
+ // markers in BaseURL (and, for non-proxy clients, the request path)
+ // against env-backed Config.TemplateVars. Done once at the top of every
+ // request so a missing env var surfaces an actionable error instead of
+ // quietly sending a literal "{shop}" to the API.
+ var endpointVars map[string]string
+ if c.Config != nil {
+ endpointVars = c.Config.TemplateVars
+ }
+{{- end}}
{{- if eq .ClientPattern "proxy-envelope"}}
// Proxy-envelope: all requests are POST'd to BaseURL with a JSON envelope
+{{- if .EndpointTemplateVars}}
+ targetURL, urlErr := buildURL(c.BaseURL, "", endpointVars)
+ if urlErr != nil {
+ return nil, 0, urlErr
+ }
+{{- else}}
targetURL := c.BaseURL
+{{- end}}
+{{- else}}
+{{- if .EndpointTemplateVars}}
+ targetURL, urlErr := buildURL(c.BaseURL, path, endpointVars)
+ if urlErr != nil {
+ return nil, 0, urlErr
+ }
{{- else}}
targetURL := c.BaseURL + path
+{{- end}}
{{- end}}
var bodyBytes []byte
← 289e5988 feat(cli): emit buildURL helper + Config.TemplateVars for te
·
back to Cli Printing Press
·
fix(cli): keep template-var env names out of verifier auth d a0e1bb4a →