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/.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/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/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())
- }
-}
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
+}
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())
+ }
+}
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
diff --git a/packages/ssestream/ssestream.go b/packages/ssestream/ssestream.go
index 02962ca..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 {
@@ -29,7 +28,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<<4)
+ decoder = &eventStreamDecoder{rc: res.Body, scn: scn}
}
return decoder
}
@@ -49,48 +50,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 +62,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 +100,10 @@ func (s *eventStreamDecoder) Next() bool {
}
}
+ if s.scn.Err() != nil {
+ s.err = s.scn.Err()
+ }
+
return false
}