From a3d995829e4bf46afc5183ed6af07a781db9b823 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Wed, 22 Jul 2026 14:21:48 -0700 Subject: [PATCH] fix(template): change UpdateTemplateArg.KeyUsage to *int to match Command API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Command's TemplateUpdateRequest.KeyUsage and TemplateRetrievalResponse.KeyUsage are both {"type":"integer","format":"int32"} per the v25.5 swagger — an int32 bitmask (e.g. 160 = digitalSignature|keyEncipherment). UpdateTemplateArg.KeyUsage was typed *bool, which serializes as a JSON boolean and produces a live HTTP 400 from Command ("Unexpected character encountered while parsing value: t. Path 'KeyUsage'"), making the field unusable as-is. GetTemplateResponse.KeyUsage was already int, so this also fixes the type mismatch between the get and update models for the same field. Also fixes the identical defect in v2/api/template_models.go for consistency; v2 is tagged/released independently and is not part of this v3.6.0 change. Adds TestUpdateTemplateArg_KeyUsage_SerializesAsInt to v3/api/template_test.go, which fails to compile against the pre-fix *bool field and asserts the wire payload is a JSON number. --- v2/api/template_models.go | 8 +++++- v3/api/template_models.go | 8 +++++- v3/api/template_test.go | 59 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/v2/api/template_models.go b/v2/api/template_models.go index 526b8c4..70eefd7 100644 --- a/v2/api/template_models.go +++ b/v2/api/template_models.go @@ -81,7 +81,13 @@ type UpdateTemplateArg struct { AllowedRequesters *[]string `json:"AllowedRequesters,omitempty"` RFCEnforcement *bool `json:"RFCEnforcement,omitempty"` RequiresApproval *bool `json:"RequiresApproval,omitempty"` - KeyUsage *bool `json:"KeyUsage,omitempty"` + // KeyUsage is an int32 bitmask on Command's wire format (e.g. 160 = + // digitalSignature|keyEncipherment), matching GetTemplateResponse.KeyUsage and + // Command's TemplateUpdateRequest/TemplateRetrievalResponse swagger schema + // (both typed "integer"/"int32"). A *bool here previously produced a live + // HTTP 400 ("Unexpected character encountered while parsing value: t. Path + // 'KeyUsage'") since Command rejects a JSON boolean for an integer field. + KeyUsage *int `json:"KeyUsage,omitempty"` } type UpdateTemplateResponse struct{ GetTemplateResponse } diff --git a/v3/api/template_models.go b/v3/api/template_models.go index 127683b..39d905b 100644 --- a/v3/api/template_models.go +++ b/v3/api/template_models.go @@ -119,7 +119,13 @@ type UpdateTemplateArg struct { AllowedRequesters *[]string `json:"AllowedRequesters,omitempty"` RFCEnforcement *bool `json:"RFCEnforcement,omitempty"` RequiresApproval *bool `json:"RequiresApproval,omitempty"` - KeyUsage *bool `json:"KeyUsage,omitempty"` + // KeyUsage is an int32 bitmask on Command's wire format (e.g. 160 = + // digitalSignature|keyEncipherment), matching GetTemplateResponse.KeyUsage and + // Command's TemplateUpdateRequest/TemplateRetrievalResponse swagger schema + // (both typed "integer"/"int32"). A *bool here previously produced a live + // HTTP 400 ("Unexpected character encountered while parsing value: t. Path + // 'KeyUsage'") since Command rejects a JSON boolean for an integer field. + KeyUsage *int `json:"KeyUsage,omitempty"` // TemplatePolicy must be round-tripped from the corresponding GetTemplateResponse // on every update; see the field comment on GetTemplateResponse.TemplatePolicy. TemplatePolicy *TemplatePolicy `json:"TemplatePolicy,omitempty"` diff --git a/v3/api/template_test.go b/v3/api/template_test.go index c18c629..ee27197 100644 --- a/v3/api/template_test.go +++ b/v3/api/template_test.go @@ -244,3 +244,62 @@ func TestUpdateTemplateArg_TemplatePolicy_Roundtrip(t *testing.T) { t.Fatalf("TemplatePolicy.PrimaryKeyAlgorithms on the wire = %v, want 2 entries", policy["PrimaryKeyAlgorithms"]) } } + +// TestUpdateTemplateArg_KeyUsage_SerializesAsInt verifies that UpdateTemplateArg.KeyUsage +// serializes onto the wire as a JSON number, matching Command's TemplateUpdateRequest +// swagger schema ({"type":"integer","format":"int32"}) confirmed against a live v25.5 +// instance. Before the fix, KeyUsage was typed *bool, which serialized as a JSON boolean +// and produced a live HTTP 400 from Command ("Unexpected character encountered while +// parsing value: t. Path 'KeyUsage'"). This also verifies the value returned by +// GetTemplateResponse.KeyUsage (an int) can be assigned directly to +// UpdateTemplateArg.KeyUsage without a type conversion, since both now agree on int. +func TestUpdateTemplateArg_KeyUsage_SerializesAsInt(t *testing.T) { + var receivedBody []byte + + srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var err error + receivedBody, err = io.ReadAll(r.Body) + if err != nil { + t.Fatalf("failed to read request body: %v", err) + } + w.Header().Set("Content-Type", "application/json") + w.Write(receivedBody) + })) + defer srv.Close() + + c := newTestClient(srv) + + // Simulate a real read-modify-write: KeyUsage comes straight off a + // GetTemplateResponse (int) with no bool<->int conversion required. + fetched := GetTemplateResponse{Id: 4, KeyUsage: 160} // digitalSignature|keyEncipherment + keyUsage := fetched.KeyUsage + + arg := &UpdateTemplateArg{ + Id: 4, + KeyUsage: &keyUsage, + } + + if _, err := c.UpdateTemplate(arg); err != nil { + t.Fatalf("UpdateTemplate() error: %v", err) + } + + var onWire map[string]interface{} + if err := json.Unmarshal(receivedBody, &onWire); err != nil { + t.Fatalf("failed to decode request body sent to server: %v", err) + } + + rawKeyUsage, ok := onWire["KeyUsage"] + if !ok { + t.Fatalf("request body sent to server has no KeyUsage field; got keys: %v", onWire) + } + switch v := rawKeyUsage.(type) { + case float64: + if v != 160 { + t.Errorf("KeyUsage on the wire = %v, want 160", v) + } + case bool: + t.Fatalf("KeyUsage on the wire is a JSON boolean (%v); Command's API expects an int32 bitmask and returns HTTP 400 for a boolean payload", v) + default: + t.Fatalf("KeyUsage on the wire has unexpected type %T (value %v), want a JSON number", v, v) + } +}