From 6fad3f9d43fbb49c718a53ae6736ac273e43b7b3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 02:30:33 +0000 Subject: [PATCH 1/7] fix(client): use scanner for streaming --- packages/ssestream/ssestream.go | 60 ++++++--------------------------- 1 file changed, 10 insertions(+), 50 deletions(-) diff --git a/packages/ssestream/ssestream.go b/packages/ssestream/ssestream.go index 02962ca..55906e7 100644 --- a/packages/ssestream/ssestream.go +++ b/packages/ssestream/ssestream.go @@ -29,7 +29,9 @@ func NewDecoder(res *http.Response) Decoder { if t, ok := decoderTypes[contentType]; ok { decoder = t(res.Body) } else { - decoder = &eventStreamDecoder{rc: res.Body, rdr: bufio.NewReader(res.Body)} + scn := bufio.NewScanner(res.Body) + scn.Buffer(nil, bufio.MaxScanTokenSize<<2) + decoder = &eventStreamDecoder{rc: res.Body, scn: scn} } return decoder } @@ -49,48 +51,10 @@ type Event struct { type eventStreamDecoder struct { evt Event rc io.ReadCloser - rdr *bufio.Reader + scn *bufio.Scanner err error } -func line(r *bufio.Reader) ([]byte, error) { - var overflow bytes.Buffer - - // To prevent infinite loops, the failsafe stops when a line is - // 100 times longer than the [io.Reader] default buffer size, - // or after 20 failed attempts to find an end of line. - for f := 0; f < 100; f++ { - part, isPrefix, err := r.ReadLine() - if err != nil { - return nil, err - } - - // Happy case, the line fits in the default buffer. - if !isPrefix && overflow.Len() == 0 { - return part, nil - } - - // Overflow case, append to the buffer. - if isPrefix || overflow.Len() > 0 { - n, err := overflow.Write(part) - if err != nil { - return nil, err - } - - // Didn't find an end of line, heavily increment the failsafe. - if n != r.Size() { - f += 5 - } - } - - if !isPrefix { - return overflow.Bytes(), nil - } - } - - return nil, fmt.Errorf("ssestream: too many attempts to read a line") -} - func (s *eventStreamDecoder) Next() bool { if s.err != nil { return false @@ -99,16 +63,8 @@ func (s *eventStreamDecoder) Next() bool { event := "" data := bytes.NewBuffer(nil) - for { - txt, err := line(s.rdr) - if err == io.EOF { - return false - } - - if err != nil { - s.err = err - break - } + for s.scn.Scan() { + txt := s.scn.Bytes() // Dispatch event on an empty line if len(txt) == 0 { @@ -145,6 +101,10 @@ func (s *eventStreamDecoder) Next() bool { } } + if s.scn.Err() != nil { + s.err = s.scn.Err() + } + return false } From eca74291efed0e090b1a7437d832ee12a819f833 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 02:50:21 +0000 Subject: [PATCH 2/7] fix(client): increase max stream buffer size --- packages/ssestream/ssestream.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ssestream/ssestream.go b/packages/ssestream/ssestream.go index 55906e7..6f8b37a 100644 --- a/packages/ssestream/ssestream.go +++ b/packages/ssestream/ssestream.go @@ -30,7 +30,7 @@ func NewDecoder(res *http.Response) Decoder { decoder = t(res.Body) } else { scn := bufio.NewScanner(res.Body) - scn.Buffer(nil, bufio.MaxScanTokenSize<<2) + scn.Buffer(nil, bufio.MaxScanTokenSize<<4) decoder = &eventStreamDecoder{rc: res.Body, scn: scn} } return decoder From 23362905d1acb3616333407f70bdee259dc66ebe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 03:00:13 +0000 Subject: [PATCH 3/7] fix(client): correctly set stream key for multipart --- appdeployment.go | 3 +++ internal/apiform/encoder.go | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/appdeployment.go b/appdeployment.go index 67ebe32..96d91ef 100644 --- a/appdeployment.go +++ b/appdeployment.go @@ -317,6 +317,9 @@ func (r AppDeploymentNewParams) MarshalMultipart() (data []byte, contentType str buf := bytes.NewBuffer(nil) writer := multipart.NewWriter(buf) err = apiform.MarshalRoot(r, writer) + if err == nil { + err = apiform.WriteExtras(writer, r.ExtraFields()) + } if err != nil { writer.Close() return nil, "", err diff --git a/internal/apiform/encoder.go b/internal/apiform/encoder.go index 84dc8eb..2002c4b 100644 --- a/internal/apiform/encoder.go +++ b/internal/apiform/encoder.go @@ -449,3 +449,17 @@ func (e *encoder) newMapEncoder(_ reflect.Type) encoderFunc { return e.encodeMapEntries(key, value, writer) } } + +func WriteExtras(writer *multipart.Writer, extras map[string]any) (err error) { + for k, v := range extras { + str, ok := v.(string) + if !ok { + break + } + err = writer.WriteField(k, str) + if err != nil { + break + } + } + return +} From 4208ce7bb382ac9bfb2cefeb406be6302df6cd7a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 03:19:25 +0000 Subject: [PATCH 4/7] fix(client): don't panic on marshal with extra null field --- internal/apijson/decoder.go | 2 +- internal/apijson/decoderesp_test.go | 30 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 internal/apijson/decoderesp_test.go diff --git a/internal/apijson/decoder.go b/internal/apijson/decoder.go index 9beba44..3bd53f7 100644 --- a/internal/apijson/decoder.go +++ b/internal/apijson/decoder.go @@ -461,7 +461,7 @@ func (d *decoderBuilder) newStructTypeDecoder(t reflect.Type) decoderFunc { } // Handle null [param.Opt] - if itemNode.Type == gjson.Null && dest.Type().Implements(reflect.TypeOf((*param.Optional)(nil)).Elem()) { + if itemNode.Type == gjson.Null && dest.IsValid() && dest.Type().Implements(reflect.TypeOf((*param.Optional)(nil)).Elem()) { dest.Addr().Interface().(json.Unmarshaler).UnmarshalJSON([]byte(itemNode.Raw)) continue } diff --git a/internal/apijson/decoderesp_test.go b/internal/apijson/decoderesp_test.go new file mode 100644 index 0000000..0453c68 --- /dev/null +++ b/internal/apijson/decoderesp_test.go @@ -0,0 +1,30 @@ +package apijson_test + +import ( + "encoding/json" + "github.com/onkernel/kernel-go-sdk/internal/apijson" + "github.com/onkernel/kernel-go-sdk/packages/respjson" + "testing" +) + +type StructWithNullExtraField struct { + Results []string `json:"results,required"` + JSON struct { + Results respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +func (r *StructWithNullExtraField) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +func TestDecodeWithNullExtraField(t *testing.T) { + raw := `{"something_else":null}` + var dst *StructWithNullExtraField + err := json.Unmarshal([]byte(raw), &dst) + if err != nil { + t.Fatalf("error: %s", err.Error()) + } +} From 34dfea07fd8538b3a73f8ee7ea35f3a70e679b3b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 15:57:50 +0000 Subject: [PATCH 5/7] feat(api): update via SDK Studio --- .stats.yml | 8 +++---- api.md | 8 ------- app.go | 64 ----------------------------------------------------- app_test.go | 40 --------------------------------- 4 files changed, 4 insertions(+), 116 deletions(-) delete mode 100644 app_test.go diff --git a/.stats.yml b/.stats.yml index 01c41ad..f0d8544 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 7 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-c9d64df733f286f09d2203f4e3d820ce57e8d4c629c5e2db4e2bfac91fbc1598.yml -openapi_spec_hash: fa407611fc566d55f403864fbfaa6c23 -config_hash: 7f67c5b95af1e4b39525515240b72275 +configured_endpoints: 6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-19b0d17ba368f32827ee322d15a7f4ff7e1f3bbf66606fad227b3465f8ffc5ab.yml +openapi_spec_hash: 4a3cb766898e8a134ef99fe6c4c87736 +config_hash: 4dfa4d870ce0e23e31ce33ab6a53dd21 diff --git a/api.md b/api.md index eb9c93c..1452129 100644 --- a/api.md +++ b/api.md @@ -1,13 +1,5 @@ # Apps -Response Types: - -- kernel.AppListResponse - -Methods: - -- client.Apps.List(ctx context.Context, query kernel.AppListParams) ([]kernel.AppListResponse, error) - ## Deployments Response Types: diff --git a/app.go b/app.go index c3d26dc..a5d6f7a 100644 --- a/app.go +++ b/app.go @@ -3,16 +3,7 @@ package kernel import ( - "context" - "net/http" - "net/url" - - "github.com/onkernel/kernel-go-sdk/internal/apijson" - "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/param" - "github.com/onkernel/kernel-go-sdk/packages/respjson" ) // AppService contains methods and other services that help with interacting with @@ -37,58 +28,3 @@ func NewAppService(opts ...option.RequestOption) (r AppService) { r.Invocations = NewAppInvocationService(opts...) return } - -// List application versions for the authenticated user. Optionally filter by app -// name and/or version label. -func (r *AppService) List(ctx context.Context, query AppListParams, opts ...option.RequestOption) (res *[]AppListResponse, err error) { - opts = append(r.Options[:], opts...) - path := "apps" - err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...) - return -} - -// Summary of an application version. -type AppListResponse struct { - // Unique identifier for the app version - ID string `json:"id,required"` - // Name of the application - AppName string `json:"app_name,required"` - // Deployment region code - Region string `json:"region,required"` - // Version label for the application - Version string `json:"version,required"` - // Environment variables configured for this app version - EnvVars map[string]string `json:"env_vars"` - // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. - JSON struct { - ID respjson.Field - AppName respjson.Field - Region respjson.Field - Version respjson.Field - EnvVars respjson.Field - ExtraFields map[string]respjson.Field - raw string - } `json:"-"` -} - -// Returns the unmodified JSON received from the API -func (r AppListResponse) RawJSON() string { return r.JSON.raw } -func (r *AppListResponse) UnmarshalJSON(data []byte) error { - return apijson.UnmarshalRoot(data, r) -} - -type AppListParams struct { - // Filter results by application name. - AppName param.Opt[string] `query:"app_name,omitzero" json:"-"` - // Filter results by version label. - Version param.Opt[string] `query:"version,omitzero" json:"-"` - paramObj -} - -// URLQuery serializes [AppListParams]'s query parameters as `url.Values`. -func (r AppListParams) URLQuery() (v url.Values, err error) { - return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ - ArrayFormat: apiquery.ArrayQueryFormatComma, - NestedFormat: apiquery.NestedQueryFormatBrackets, - }) -} diff --git a/app_test.go b/app_test.go deleted file mode 100644 index 0b9b741..0000000 --- a/app_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -package kernel_test - -import ( - "context" - "errors" - "os" - "testing" - - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/internal/testutil" - "github.com/onkernel/kernel-go-sdk/option" -) - -func TestAppListWithOptionalParams(t *testing.T) { - t.Skip("skipped: tests are disabled for the time being") - 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"), - ) - _, err := client.Apps.List(context.TODO(), kernel.AppListParams{ - AppName: kernel.String("app_name"), - Version: kernel.String("version"), - }) - if err != nil { - var apierr *kernel.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} From a95799341aec82be4887fd8ba537442be2472176 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 15:58:06 +0000 Subject: [PATCH 6/7] release: 0.1.0-alpha.4 --- .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 aaf968a..b56c3d0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0-alpha.3" + ".": "0.1.0-alpha.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a8f222a..6ac757e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.1.0-alpha.4 (2025-05-20) + +Full Changelog: [v0.1.0-alpha.3...v0.1.0-alpha.4](https://github.com/onkernel/kernel-go-sdk/compare/v0.1.0-alpha.3...v0.1.0-alpha.4) + +### Features + +* **api:** update via SDK Studio ([34dfea0](https://github.com/onkernel/kernel-go-sdk/commit/34dfea07fd8538b3a73f8ee7ea35f3a70e679b3b)) + + +### Bug Fixes + +* **client:** correctly set stream key for multipart ([2336290](https://github.com/onkernel/kernel-go-sdk/commit/23362905d1acb3616333407f70bdee259dc66ebe)) +* **client:** don't panic on marshal with extra null field ([4208ce7](https://github.com/onkernel/kernel-go-sdk/commit/4208ce7bb382ac9bfb2cefeb406be6302df6cd7a)) +* **client:** increase max stream buffer size ([eca7429](https://github.com/onkernel/kernel-go-sdk/commit/eca74291efed0e090b1a7437d832ee12a819f833)) +* **client:** use scanner for streaming ([6fad3f9](https://github.com/onkernel/kernel-go-sdk/commit/6fad3f9d43fbb49c718a53ae6736ac273e43b7b3)) + ## 0.1.0-alpha.3 (2025-05-19) Full Changelog: [v0.1.0-alpha.2...v0.1.0-alpha.3](https://github.com/onkernel/kernel-go-sdk/compare/v0.1.0-alpha.2...v0.1.0-alpha.3) diff --git a/README.md b/README.md index b2add49..5ec3f4e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Or to pin the version: ```sh -go get -u 'github.com/onkernel/kernel-go-sdk@v0.1.0-alpha.3' +go get -u 'github.com/onkernel/kernel-go-sdk@v0.1.0-alpha.4' ``` diff --git a/internal/version.go b/internal/version.go index 2d1d85e..5469df6 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.1.0-alpha.3" // x-release-please-version +const PackageVersion = "0.1.0-alpha.4" // x-release-please-version From 2c779acd3aa30ac616c30e85c2e10ef047b6233b Mon Sep 17 00:00:00 2001 From: Rafael Date: Tue, 20 May 2025 12:01:10 -0400 Subject: [PATCH 7/7] Update ssestream.go --- packages/ssestream/ssestream.go | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ssestream/ssestream.go b/packages/ssestream/ssestream.go index 6f8b37a..81adbd6 100644 --- a/packages/ssestream/ssestream.go +++ b/packages/ssestream/ssestream.go @@ -9,7 +9,6 @@ import ( "io" "net/http" "strings" - "fmt" ) type Decoder interface {