From 9130f8c40783560bc02d5b494c60549dbe092dcd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:16:39 +0000 Subject: [PATCH 1/6] feat(api): add pagination to the deployments endpoint --- .stats.yml | 6 +- README.md | 31 ++++++++ api.md | 2 +- deployment.go | 28 ++++++- deployment_test.go | 4 +- packages/pagination/pagination.go | 127 ++++++++++++++++++++++++++++++ paginationauto_test.go | 39 +++++++++ paginationmanual_test.go | 47 +++++++++++ 8 files changed, 275 insertions(+), 9 deletions(-) create mode 100644 packages/pagination/pagination.go create mode 100644 paginationauto_test.go create mode 100644 paginationmanual_test.go diff --git a/.stats.yml b/.stats.yml index 6ac19ba..8943c2f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 46 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-e98d46c55826cdf541a9ee0df04ce92806ac6d4d92957ae79f897270b7d85b23.yml -openapi_spec_hash: 8a1af54fc0a4417165b8a52e6354b685 -config_hash: 043ddc54629c6d8b889123770cb4769f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-33f1feaba7bde46bfa36d2fefb5c3bc9512967945bccf78045ad3f64aafc4eb0.yml +openapi_spec_hash: 52a448889d41216d1ca30e8a57115b14 +config_hash: 1f28d5c3c063f418ebd2799df1e4e781 diff --git a/README.md b/README.md index 0b271ea..e4616d5 100644 --- a/README.md +++ b/README.md @@ -289,9 +289,40 @@ This library provides some conveniences for working with paginated list endpoint You can use `.ListAutoPaging()` methods to iterate through items across all pages: +```go +iter := client.Deployments.ListAutoPaging(context.TODO(), kernel.DeploymentListParams{ + AppName: "YOUR_APP", + Limit: kernel.Int(2), +}) +// Automatically fetches more pages as needed. +for iter.Next() { + deploymentListResponse := iter.Current() + fmt.Printf("%+v\n", deploymentListResponse) +} +if err := iter.Err(); err != nil { + panic(err.Error()) +} +``` + Or you can use simple `.List()` methods to fetch a single page and receive a standard response object with additional helper methods like `.GetNextPage()`, e.g.: +```go +page, err := client.Deployments.List(context.TODO(), kernel.DeploymentListParams{ + AppName: "YOUR_APP", + Limit: kernel.Int(2), +}) +for page != nil { + for _, deployment := range page.Items { + fmt.Printf("%+v\n", deployment) + } + page, err = page.GetNextPage() +} +if err != nil { + panic(err.Error()) +} +``` + ### Errors When the API returns a non-success status code, we return an error with type diff --git a/api.md b/api.md index 0efea8f..30dabbc 100644 --- a/api.md +++ b/api.md @@ -21,7 +21,7 @@ Methods: - client.Deployments.New(ctx context.Context, body kernel.DeploymentNewParams) (kernel.DeploymentNewResponse, error) - client.Deployments.Get(ctx context.Context, id string) (kernel.DeploymentGetResponse, error) -- client.Deployments.List(ctx context.Context, query kernel.DeploymentListParams) ([]kernel.DeploymentListResponse, error) +- client.Deployments.List(ctx context.Context, query kernel.DeploymentListParams) (pagination.OffsetPagination[kernel.DeploymentListResponse], error) - client.Deployments.Follow(ctx context.Context, id string, query kernel.DeploymentFollowParams) (kernel.DeploymentFollowResponseUnion, error) # Apps diff --git a/deployment.go b/deployment.go index 9409a69..903db2e 100644 --- a/deployment.go +++ b/deployment.go @@ -19,6 +19,7 @@ import ( "github.com/onkernel/kernel-go-sdk/internal/apiquery" "github.com/onkernel/kernel-go-sdk/internal/requestconfig" "github.com/onkernel/kernel-go-sdk/option" + "github.com/onkernel/kernel-go-sdk/packages/pagination" "github.com/onkernel/kernel-go-sdk/packages/param" "github.com/onkernel/kernel-go-sdk/packages/respjson" "github.com/onkernel/kernel-go-sdk/packages/ssestream" @@ -66,11 +67,26 @@ func (r *DeploymentService) Get(ctx context.Context, id string, opts ...option.R } // List deployments. Optionally filter by application name. -func (r *DeploymentService) List(ctx context.Context, query DeploymentListParams, opts ...option.RequestOption) (res *[]DeploymentListResponse, err error) { +func (r *DeploymentService) List(ctx context.Context, query DeploymentListParams, opts ...option.RequestOption) (res *pagination.OffsetPagination[DeploymentListResponse], err error) { + var raw *http.Response opts = append(r.Options[:], opts...) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) path := "deployments" - err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...) - return + cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodGet, path, query, &res, opts...) + if err != nil { + return nil, err + } + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +// List deployments. Optionally filter by application name. +func (r *DeploymentService) ListAutoPaging(ctx context.Context, query DeploymentListParams, opts ...option.RequestOption) *pagination.OffsetPaginationAutoPager[DeploymentListResponse] { + return pagination.NewOffsetPaginationAutoPager(r.List(ctx, query, opts...)) } // Establishes a Server-Sent Events (SSE) stream that delivers real-time logs and @@ -476,7 +492,11 @@ const ( type DeploymentListParams struct { // Filter results by application name. - AppName param.Opt[string] `query:"app_name,omitzero" json:"-"` + AppName string `query:"app_name,required" json:"-"` + // Limit the number of deployments to return. + Limit param.Opt[int64] `query:"limit,omitzero" json:"-"` + // Offset the number of deployments to return. + Offset param.Opt[int64] `query:"offset,omitzero" json:"-"` paramObj } diff --git a/deployment_test.go b/deployment_test.go index 931721c..03b148f 100644 --- a/deployment_test.go +++ b/deployment_test.go @@ -84,7 +84,9 @@ func TestDeploymentListWithOptionalParams(t *testing.T) { option.WithAPIKey("My API Key"), ) _, err := client.Deployments.List(context.TODO(), kernel.DeploymentListParams{ - AppName: kernel.String("app_name"), + AppName: "app_name", + Limit: kernel.Int(1), + Offset: kernel.Int(0), }) if err != nil { var apierr *kernel.Error diff --git a/packages/pagination/pagination.go b/packages/pagination/pagination.go new file mode 100644 index 0000000..739e57f --- /dev/null +++ b/packages/pagination/pagination.go @@ -0,0 +1,127 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package pagination + +import ( + "net/http" + "strconv" + + "github.com/onkernel/kernel-go-sdk/internal/apijson" + "github.com/onkernel/kernel-go-sdk/internal/requestconfig" + "github.com/onkernel/kernel-go-sdk/option" + "github.com/onkernel/kernel-go-sdk/packages/param" + "github.com/onkernel/kernel-go-sdk/packages/respjson" +) + +// aliased to make [param.APIUnion] private when embedding +type paramUnion = param.APIUnion + +// aliased to make [param.APIObject] private when embedding +type paramObj = param.APIObject + +type OffsetPagination[T any] struct { + Items []T `json:",inline"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Items respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` + cfg *requestconfig.RequestConfig + res *http.Response +} + +// Returns the unmodified JSON received from the API +func (r OffsetPagination[T]) RawJSON() string { return r.JSON.raw } +func (r *OffsetPagination[T]) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// GetNextPage returns the next page as defined by this pagination style. When +// there is no next page, this function will return a 'nil' for the page value, but +// will not return an error +func (r *OffsetPagination[T]) GetNextPage() (res *OffsetPagination[T], err error) { + if len(r.Items) == 0 { + return nil, nil + } + cfg := r.cfg.Clone(r.cfg.Context) + + q := cfg.Request.URL.Query() + offset, err := strconv.ParseInt(q.Get("offset"), 10, 64) + if err != nil { + offset = 0 + } + length := int64(len(r.Items)) + next := offset + length + + if length > 0 && next != 0 { + err = cfg.Apply(option.WithQuery("offset", strconv.FormatInt(next, 10))) + if err != nil { + return nil, err + } + } else { + return nil, nil + } + var raw *http.Response + cfg.ResponseInto = &raw + cfg.ResponseBodyInto = &res + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +func (r *OffsetPagination[T]) SetPageConfig(cfg *requestconfig.RequestConfig, res *http.Response) { + if r == nil { + r = &OffsetPagination[T]{} + } + r.cfg = cfg + r.res = res +} + +type OffsetPaginationAutoPager[T any] struct { + page *OffsetPagination[T] + cur T + idx int + run int + err error + paramObj +} + +func NewOffsetPaginationAutoPager[T any](page *OffsetPagination[T], err error) *OffsetPaginationAutoPager[T] { + return &OffsetPaginationAutoPager[T]{ + page: page, + err: err, + } +} + +func (r *OffsetPaginationAutoPager[T]) Next() bool { + if r.page == nil || len(r.page.Items) == 0 { + return false + } + if r.idx >= len(r.page.Items) { + r.idx = 0 + r.page, r.err = r.page.GetNextPage() + if r.err != nil || r.page == nil || len(r.page.Items) == 0 { + return false + } + } + r.cur = r.page.Items[r.idx] + r.run += 1 + r.idx += 1 + return true +} + +func (r *OffsetPaginationAutoPager[T]) Current() T { + return r.cur +} + +func (r *OffsetPaginationAutoPager[T]) Err() error { + return r.err +} + +func (r *OffsetPaginationAutoPager[T]) Index() int { + return r.run +} diff --git a/paginationauto_test.go b/paginationauto_test.go new file mode 100644 index 0000000..e3003e1 --- /dev/null +++ b/paginationauto_test.go @@ -0,0 +1,39 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel_test + +import ( + "context" + "os" + "testing" + + "github.com/onkernel/kernel-go-sdk" + "github.com/onkernel/kernel-go-sdk/internal/testutil" + "github.com/onkernel/kernel-go-sdk/option" +) + +func TestAutoPagination(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + iter := client.Deployments.ListAutoPaging(context.TODO(), kernel.DeploymentListParams{ + AppName: "YOUR_APP", + Limit: kernel.Int(2), + }) + // Prism mock isn't going to give us real pagination + for i := 0; i < 3 && iter.Next(); i++ { + deployment := iter.Current() + t.Logf("%+v\n", deployment.ID) + } + if err := iter.Err(); err != nil { + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/paginationmanual_test.go b/paginationmanual_test.go new file mode 100644 index 0000000..53d6156 --- /dev/null +++ b/paginationmanual_test.go @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel_test + +import ( + "context" + "os" + "testing" + + "github.com/onkernel/kernel-go-sdk" + "github.com/onkernel/kernel-go-sdk/internal/testutil" + "github.com/onkernel/kernel-go-sdk/option" +) + +func TestManualPagination(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := kernel.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + page, err := client.Deployments.List(context.TODO(), kernel.DeploymentListParams{ + AppName: "YOUR_APP", + Limit: kernel.Int(2), + }) + if err != nil { + t.Fatalf("err should be nil: %s", err.Error()) + } + for _, deployment := range page.Items { + t.Logf("%+v\n", deployment.ID) + } + // Prism mock isn't going to give us real pagination + page, err = page.GetNextPage() + if err != nil { + t.Fatalf("err should be nil: %s", err.Error()) + } + if page != nil { + for _, deployment := range page.Items { + t.Logf("%+v\n", deployment.ID) + } + } +} From ad37eb2bdc92a4145e5dba9d8ef8b4ad26c9b64a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:19:32 +0000 Subject: [PATCH 2/6] feat(api): update API spec with pagination headers --- .stats.yml | 4 ++-- README.md | 4 ++-- deployment.go | 2 +- deployment_test.go | 2 +- paginationauto_test.go | 2 +- paginationmanual_test.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8943c2f..9635bb2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 46 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-33f1feaba7bde46bfa36d2fefb5c3bc9512967945bccf78045ad3f64aafc4eb0.yml -openapi_spec_hash: 52a448889d41216d1ca30e8a57115b14 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-cb38560915edce03abce2ae3ef5bc745489dbe9b6f80c2b4ff42edf8c2ff276d.yml +openapi_spec_hash: a869194d6c864ba28d79ec0105439c3e config_hash: 1f28d5c3c063f418ebd2799df1e4e781 diff --git a/README.md b/README.md index e4616d5..b66f647 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ You can use `.ListAutoPaging()` methods to iterate through items across all page ```go iter := client.Deployments.ListAutoPaging(context.TODO(), kernel.DeploymentListParams{ - AppName: "YOUR_APP", + AppName: kernel.String("YOUR_APP"), Limit: kernel.Int(2), }) // Automatically fetches more pages as needed. @@ -309,7 +309,7 @@ with additional helper methods like `.GetNextPage()`, e.g.: ```go page, err := client.Deployments.List(context.TODO(), kernel.DeploymentListParams{ - AppName: "YOUR_APP", + AppName: kernel.String("YOUR_APP"), Limit: kernel.Int(2), }) for page != nil { diff --git a/deployment.go b/deployment.go index 903db2e..368c538 100644 --- a/deployment.go +++ b/deployment.go @@ -492,7 +492,7 @@ const ( type DeploymentListParams struct { // Filter results by application name. - AppName string `query:"app_name,required" json:"-"` + AppName param.Opt[string] `query:"app_name,omitzero" json:"-"` // Limit the number of deployments to return. Limit param.Opt[int64] `query:"limit,omitzero" json:"-"` // Offset the number of deployments to return. diff --git a/deployment_test.go b/deployment_test.go index 03b148f..81715eb 100644 --- a/deployment_test.go +++ b/deployment_test.go @@ -84,7 +84,7 @@ func TestDeploymentListWithOptionalParams(t *testing.T) { option.WithAPIKey("My API Key"), ) _, err := client.Deployments.List(context.TODO(), kernel.DeploymentListParams{ - AppName: "app_name", + AppName: kernel.String("app_name"), Limit: kernel.Int(1), Offset: kernel.Int(0), }) diff --git a/paginationauto_test.go b/paginationauto_test.go index e3003e1..d407e2d 100644 --- a/paginationauto_test.go +++ b/paginationauto_test.go @@ -25,7 +25,7 @@ func TestAutoPagination(t *testing.T) { option.WithAPIKey("My API Key"), ) iter := client.Deployments.ListAutoPaging(context.TODO(), kernel.DeploymentListParams{ - AppName: "YOUR_APP", + AppName: kernel.String("YOUR_APP"), Limit: kernel.Int(2), }) // Prism mock isn't going to give us real pagination diff --git a/paginationmanual_test.go b/paginationmanual_test.go index 53d6156..136e786 100644 --- a/paginationmanual_test.go +++ b/paginationmanual_test.go @@ -25,7 +25,7 @@ func TestManualPagination(t *testing.T) { option.WithAPIKey("My API Key"), ) page, err := client.Deployments.List(context.TODO(), kernel.DeploymentListParams{ - AppName: "YOUR_APP", + AppName: kernel.String("YOUR_APP"), Limit: kernel.Int(2), }) if err != nil { From 65c53429690169c3ca5068b94991af6eba3832ff Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Sep 2025 18:00:26 +0000 Subject: [PATCH 3/6] feat(api): pagination properties added to response (has_more, next_offset) --- .stats.yml | 2 +- packages/pagination/pagination.go | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9635bb2..7fb3d31 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 46 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-cb38560915edce03abce2ae3ef5bc745489dbe9b6f80c2b4ff42edf8c2ff276d.yml openapi_spec_hash: a869194d6c864ba28d79ec0105439c3e -config_hash: 1f28d5c3c063f418ebd2799df1e4e781 +config_hash: ed56f95781ec9b2e73c97e1a66606071 diff --git a/packages/pagination/pagination.go b/packages/pagination/pagination.go index 739e57f..6181b12 100644 --- a/packages/pagination/pagination.go +++ b/packages/pagination/pagination.go @@ -46,13 +46,8 @@ func (r *OffsetPagination[T]) GetNextPage() (res *OffsetPagination[T], err error } cfg := r.cfg.Clone(r.cfg.Context) - q := cfg.Request.URL.Query() - offset, err := strconv.ParseInt(q.Get("offset"), 10, 64) - if err != nil { - offset = 0 - } + next := r.res.Header.Get("X-Next-Offset") length := int64(len(r.Items)) - next := offset + length if length > 0 && next != 0 { err = cfg.Apply(option.WithQuery("offset", strconv.FormatInt(next, 10))) From d42bd8a79dc3f64ea6aa8c3f4239b8cfa8545d82 Mon Sep 17 00:00:00 2001 From: David Meadows Date: Fri, 5 Sep 2025 16:18:14 -0400 Subject: [PATCH 4/6] fix(client): correctly convert header pagination value to int --- packages/pagination/pagination.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/pagination/pagination.go b/packages/pagination/pagination.go index 6181b12..abd8460 100644 --- a/packages/pagination/pagination.go +++ b/packages/pagination/pagination.go @@ -46,7 +46,11 @@ func (r *OffsetPagination[T]) GetNextPage() (res *OffsetPagination[T], err error } cfg := r.cfg.Clone(r.cfg.Context) - next := r.res.Header.Get("X-Next-Offset") + nextStr := r.res.Header.Get("X-Next-Offset") + next, err := strconv.ParseInt(nextStr, 10, 64) + if err != nil { + return nil, err + } length := int64(len(r.Items)) if length > 0 && next != 0 { From 649203d6621f6850ac4cb414b04b78c9693d1923 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Sep 2025 03:50:37 +0000 Subject: [PATCH 5/6] fix(internal): unmarshal correctly when there are multiple discriminators --- internal/apijson/decodeparam_test.go | 88 ++++++++++++++++++++++++++++ internal/apijson/union.go | 48 ++++++++------- 2 files changed, 115 insertions(+), 21 deletions(-) diff --git a/internal/apijson/decodeparam_test.go b/internal/apijson/decodeparam_test.go index 7abe5d8..39b8318 100644 --- a/internal/apijson/decodeparam_test.go +++ b/internal/apijson/decodeparam_test.go @@ -351,6 +351,36 @@ func init() { }) } +type FooVariant struct { + Type string `json:"type,required"` + Value string `json:"value,required"` +} + +type BarVariant struct { + Type string `json:"type,required"` + Enable bool `json:"enable,required"` +} + +type MultiDiscriminatorUnion struct { + OfFoo *FooVariant `json:",inline"` + OfBar *BarVariant `json:",inline"` + + paramUnion +} + +func init() { + apijson.RegisterDiscriminatedUnion[MultiDiscriminatorUnion]("type", map[string]reflect.Type{ + "foo": reflect.TypeOf(FooVariant{}), + "foo_v2": reflect.TypeOf(FooVariant{}), + "bar": reflect.TypeOf(BarVariant{}), + "bar_legacy": reflect.TypeOf(BarVariant{}), + }) +} + +func (m *MultiDiscriminatorUnion) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, m) +} + func (d *DiscriminatedUnion) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, d) } @@ -408,3 +438,61 @@ func TestDiscriminatedUnion(t *testing.T) { }) } } + +func TestMultiDiscriminatorUnion(t *testing.T) { + tests := map[string]struct { + raw string + target MultiDiscriminatorUnion + shouldFail bool + }{ + "foo_variant": { + raw: `{"type":"foo","value":"test"}`, + target: MultiDiscriminatorUnion{OfFoo: &FooVariant{ + Type: "foo", + Value: "test", + }}, + }, + "foo_v2_variant": { + raw: `{"type":"foo_v2","value":"test_v2"}`, + target: MultiDiscriminatorUnion{OfFoo: &FooVariant{ + Type: "foo_v2", + Value: "test_v2", + }}, + }, + "bar_variant": { + raw: `{"type":"bar","enable":true}`, + target: MultiDiscriminatorUnion{OfBar: &BarVariant{ + Type: "bar", + Enable: true, + }}, + }, + "bar_legacy_variant": { + raw: `{"type":"bar_legacy","enable":false}`, + target: MultiDiscriminatorUnion{OfBar: &BarVariant{ + Type: "bar_legacy", + Enable: false, + }}, + }, + "invalid_type": { + raw: `{"type":"unknown","value":"test"}`, + target: MultiDiscriminatorUnion{}, + shouldFail: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + var dst MultiDiscriminatorUnion + err := json.Unmarshal([]byte(test.raw), &dst) + if err != nil && !test.shouldFail { + t.Fatalf("failed unmarshal with err: %v", err) + } + if err == nil && test.shouldFail { + t.Fatalf("expected unmarshal to fail but it succeeded") + } + if !reflect.DeepEqual(dst, test.target) { + t.Fatalf("failed equality, got %#v but expected %#v", dst, test.target) + } + }) + } +} diff --git a/internal/apijson/union.go b/internal/apijson/union.go index b9b8bda..9d717e7 100644 --- a/internal/apijson/union.go +++ b/internal/apijson/union.go @@ -39,12 +39,10 @@ func RegisterDiscriminatedUnion[T any](key string, mappings map[string]reflect.T func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc { type variantDecoder struct { - decoder decoderFunc - field reflect.StructField - discriminatorValue any + decoder decoderFunc + field reflect.StructField } - - variants := []variantDecoder{} + decoders := []variantDecoder{} for i := 0; i < t.NumField(); i++ { field := t.Field(i) @@ -53,18 +51,26 @@ func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc { } decoder := d.typeDecoder(field.Type) - variants = append(variants, variantDecoder{ + decoders = append(decoders, variantDecoder{ decoder: decoder, field: field, }) } + type discriminatedDecoder struct { + variantDecoder + discriminator any + } + discriminatedDecoders := []discriminatedDecoder{} unionEntry, discriminated := unionRegistry[t] - for _, unionVariant := range unionEntry.variants { - for i := 0; i < len(variants); i++ { - variant := &variants[i] - if variant.field.Type.Elem() == unionVariant.Type { - variant.discriminatorValue = unionVariant.DiscriminatorValue + for _, variant := range unionEntry.variants { + // For each union variant, find a matching decoder and save it + for _, decoder := range decoders { + if decoder.field.Type.Elem() == variant.Type { + discriminatedDecoders = append(discriminatedDecoders, discriminatedDecoder{ + decoder, + variant.DiscriminatorValue, + }) break } } @@ -73,10 +79,10 @@ func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc { return func(n gjson.Result, v reflect.Value, state *decoderState) error { if discriminated && n.Type == gjson.JSON && len(unionEntry.discriminatorKey) != 0 { discriminator := n.Get(unionEntry.discriminatorKey).Value() - for _, variant := range variants { - if discriminator == variant.discriminatorValue { - inner := v.FieldByIndex(variant.field.Index) - return variant.decoder(n, inner, state) + for _, decoder := range discriminatedDecoders { + if discriminator == decoder.discriminator { + inner := v.FieldByIndex(decoder.field.Index) + return decoder.decoder(n, inner, state) } } return errors.New("apijson: was not able to find discriminated union variant") @@ -85,15 +91,15 @@ func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc { // Set bestExactness to worse than loose bestExactness := loose - 1 bestVariant := -1 - for i, variant := range variants { + for i, decoder := range decoders { // Pointers are used to discern JSON object variants from value variants - if n.Type != gjson.JSON && variant.field.Type.Kind() == reflect.Ptr { + if n.Type != gjson.JSON && decoder.field.Type.Kind() == reflect.Ptr { continue } sub := decoderState{strict: state.strict, exactness: exact} - inner := v.FieldByIndex(variant.field.Index) - err := variant.decoder(n, inner, &sub) + inner := v.FieldByIndex(decoder.field.Index) + err := decoder.decoder(n, inner, &sub) if err != nil { continue } @@ -116,11 +122,11 @@ func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc { return errors.New("apijson: was not able to coerce type as union strictly") } - for i := 0; i < len(variants); i++ { + for i := 0; i < len(decoders); i++ { if i == bestVariant { continue } - v.FieldByIndex(variants[i].field.Index).SetZero() + v.FieldByIndex(decoders[i].field.Index).SetZero() } return nil From 2a425e88c53788014f77816778063db37d646078 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 6 Sep 2025 03:50:54 +0000 Subject: [PATCH 6/6] release: 0.11.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ README.md | 2 +- internal/version.go | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f7014c3..e82003f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.11.0" + ".": "0.11.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b890e4e..2ea13b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.11.1 (2025-09-06) + +Full Changelog: [v0.11.0...v0.11.1](https://github.com/onkernel/kernel-go-sdk/compare/v0.11.0...v0.11.1) + +### Features + +* **api:** add pagination to the deployments endpoint ([9130f8c](https://github.com/onkernel/kernel-go-sdk/commit/9130f8c40783560bc02d5b494c60549dbe092dcd)) +* **api:** pagination properties added to response (has_more, next_offset) ([65c5342](https://github.com/onkernel/kernel-go-sdk/commit/65c53429690169c3ca5068b94991af6eba3832ff)) +* **api:** update API spec with pagination headers ([ad37eb2](https://github.com/onkernel/kernel-go-sdk/commit/ad37eb2bdc92a4145e5dba9d8ef8b4ad26c9b64a)) + + +### Bug Fixes + +* **client:** correctly convert header pagination value to int ([d42bd8a](https://github.com/onkernel/kernel-go-sdk/commit/d42bd8a79dc3f64ea6aa8c3f4239b8cfa8545d82)) +* **internal:** unmarshal correctly when there are multiple discriminators ([649203d](https://github.com/onkernel/kernel-go-sdk/commit/649203d6621f6850ac4cb414b04b78c9693d1923)) + ## 0.11.0 (2025-09-04) Full Changelog: [v0.10.0...v0.11.0](https://github.com/onkernel/kernel-go-sdk/compare/v0.10.0...v0.11.0) diff --git a/README.md b/README.md index b66f647..083f30a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Or to pin the version: ```sh -go get -u 'github.com/onkernel/kernel-go-sdk@v0.11.0' +go get -u 'github.com/onkernel/kernel-go-sdk@v0.11.1' ``` diff --git a/internal/version.go b/internal/version.go index 153db11..8985cf3 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.11.0" // x-release-please-version +const PackageVersion = "0.11.1" // x-release-please-version