Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/param/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
18 changes: 18 additions & 0 deletions packages/param/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
11 changes: 11 additions & 0 deletions packages/param/param.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down