Skip to content

Commit 9690d6b

Browse files
feat(client): add escape hatch to omit required param fields (#354)
1 parent dc246a6 commit 9690d6b

File tree

3 files changed

+37
-1
lines changed

3 files changed

+37
-1
lines changed

Diff for: packages/param/encoder.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ func MarshalObject[T OverridableObject](f T, underlying any) ([]byte, error) {
3333
return nil, err
3434
}
3535
for k, v := range extras {
36-
bytes, err = sjson.SetBytes(bytes, k, v)
36+
if v == Omit {
37+
// Errors handling ForceOmitted are ignored.
38+
if b, e := sjson.DeleteBytes(bytes, k); e == nil {
39+
bytes = b
40+
}
41+
} else {
42+
bytes, err = sjson.SetBytes(bytes, k, v)
43+
}
3744
if err != nil {
3845
return nil, err
3946
}

Diff for: packages/param/encoder_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,24 @@ func TestExtraFields(t *testing.T) {
117117
}
118118
}
119119

120+
func TestExtraFieldsForceOmitted(t *testing.T) {
121+
v := Struct{
122+
// Testing with the zero value.
123+
// A: "",
124+
// B: 0,
125+
}
126+
v.WithExtraFields(map[string]any{
127+
"b": param.Omit,
128+
})
129+
bytes, err := json.Marshal(v)
130+
if err != nil {
131+
t.Fatalf("failed to marshal: %v", err)
132+
}
133+
if string(bytes) != `{"a":""}` {
134+
t.Fatalf("failed to marshal: got %v", string(bytes))
135+
}
136+
}
137+
120138
type UnionWithDates struct {
121139
OfDate param.Opt[time.Time]
122140
OfTime param.Opt[time.Time]

Diff for: packages/param/param.go

+11
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ type APIObject struct{ metadata }
6262
// APIUnion should be embedded in all api unions fields, preferably using an alias to make private
6363
type APIUnion struct{ metadata }
6464

65+
type forceOmit int
66+
67+
// Omit can be used with [metadata.WithExtraFields] to ensure that a
68+
// required field is omitted. This is useful as an escape hatch for
69+
// when a required is unwanted for some unexpected reason.
70+
const Omit forceOmit = -1
71+
6572
type metadata struct{ any }
6673
type metadataNull struct{}
6774
type metadataExtraFields map[string]any
@@ -98,6 +105,10 @@ func (m metadata) GetExtraFields() map[string]any {
98105
//
99106
// WithExtraFields will override any existing fields with the same key.
100107
// For security reasons, ensure this is only used with trusted input data.
108+
//
109+
// To intentionally omit a required field, use [Omit].
110+
//
111+
// foo.WithExtraFields(map[string]any{"bar": Omit})
101112
func (m *metadata) WithExtraFields(extraFields map[string]any) {
102113
m.any = metadataExtraFields(extraFields)
103114
}

0 commit comments

Comments
 (0)