From dc8f2e6bc164bbefcf138444481e217d2ae15b5b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 21:36:51 +0000 Subject: [PATCH] feat(client): add escape hatch to omit required param fields --- packages/param/encoder.go | 9 ++++++++- packages/param/encoder_test.go | 18 ++++++++++++++++++ packages/param/param.go | 11 +++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/param/encoder.go b/packages/param/encoder.go index 97bf07db..f88a64e4 100644 --- a/packages/param/encoder.go +++ b/packages/param/encoder.go @@ -33,7 +33,14 @@ func MarshalObject[T OverridableObject](f T, underlying any) ([]byte, error) { return nil, err } for k, v := range extras { - bytes, err = sjson.SetBytes(bytes, k, v) + if v == Omit { + // Errors handling ForceOmitted are ignored. + if b, e := sjson.DeleteBytes(bytes, k); e == nil { + bytes = b + } + } else { + bytes, err = sjson.SetBytes(bytes, k, v) + } if err != nil { return nil, err } diff --git a/packages/param/encoder_test.go b/packages/param/encoder_test.go index 3dce9ceb..8ba62c15 100644 --- a/packages/param/encoder_test.go +++ b/packages/param/encoder_test.go @@ -117,6 +117,24 @@ func TestExtraFields(t *testing.T) { } } +func TestExtraFieldsForceOmitted(t *testing.T) { + v := Struct{ + // Testing with the zero value. + // A: "", + // B: 0, + } + v.WithExtraFields(map[string]any{ + "b": param.Omit, + }) + bytes, err := json.Marshal(v) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + if string(bytes) != `{"a":""}` { + t.Fatalf("failed to marshal: got %v", string(bytes)) + } +} + type UnionWithDates struct { OfDate param.Opt[time.Time] OfTime param.Opt[time.Time] diff --git a/packages/param/param.go b/packages/param/param.go index cafcafac..cfbcd81a 100644 --- a/packages/param/param.go +++ b/packages/param/param.go @@ -62,6 +62,13 @@ type APIObject struct{ metadata } // APIUnion should be embedded in all api unions fields, preferably using an alias to make private type APIUnion struct{ metadata } +type forceOmit int + +// Omit can be used with [metadata.WithExtraFields] to ensure that a +// required field is omitted. This is useful as an escape hatch for +// when a required is unwanted for some unexpected reason. +const Omit forceOmit = -1 + type metadata struct{ any } type metadataNull struct{} type metadataExtraFields map[string]any @@ -98,6 +105,10 @@ func (m metadata) GetExtraFields() map[string]any { // // WithExtraFields will override any existing fields with the same key. // For security reasons, ensure this is only used with trusted input data. +// +// To intentionally omit a required field, use [Omit]. +// +// foo.WithExtraFields(map[string]any{"bar": Omit}) func (m *metadata) WithExtraFields(extraFields map[string]any) { m.any = metadataExtraFields(extraFields) }