Skip to content
Open
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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.0.2"
".": "0.1.0"
}
2 changes: 1 addition & 1 deletion .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 14
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/miru-ml%2Fmiru-server-e8b887c478291aecbbf06e532903242a5d5ff682a2a6814921bb770c97a1753d.yml
openapi_spec_hash: b15141c2e0e81b8029c620d4aef4188c
config_hash: 3de34a47f7bb67e784afed4170b209b1
config_hash: 95b6fa4e87744247f5bdbf841c16ce0a
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
# Changelog

## 0.1.0 (2025-12-19)

Full Changelog: [v0.0.2...v0.1.0](https://github.com/miruml/go-server-sdk/compare/v0.0.2...v0.1.0)

### ⚠ BREAKING CHANGES

* **api:** change route to be /beta instead of /v1

### Features

* **encoder:** support bracket encoding form-data object members ([42cd12f](https://github.com/miruml/go-server-sdk/commit/42cd12f63596c6f1bc5aa86d621f6929716b6ac8))


### Bug Fixes

* **mcp:** correct code tool API endpoint ([c2128e7](https://github.com/miruml/go-server-sdk/commit/c2128e744bbd9473c8a1b6bef9cee4c3e1984558))
* rename param to avoid collision ([38be6e4](https://github.com/miruml/go-server-sdk/commit/38be6e4729020e74ec2ad2c6f3b7506c3e6db4b2))
* skip usage tests that don't work with Prism ([3a4ce7e](https://github.com/miruml/go-server-sdk/commit/3a4ce7e207e17b8730279da7d1de116ed8005ff3))


### Chores

* add float64 to valid types for RegisterFieldValidator ([dbeb503](https://github.com/miruml/go-server-sdk/commit/dbeb503b9299414959b2ce494ab88b3d552a4615))
* bump gjson version ([561ebe2](https://github.com/miruml/go-server-sdk/commit/561ebe292506a695c7c6722a558efc4a57a4e7ca))
* elide duplicate aliases ([362f943](https://github.com/miruml/go-server-sdk/commit/362f943009b5ee378163d4db6473150fc301b82b))
* fix empty interfaces ([74e4e50](https://github.com/miruml/go-server-sdk/commit/74e4e50593ba2d1e56e6671d0d69365e766f62a6))
* **internal:** codegen related update ([60b4b3c](https://github.com/miruml/go-server-sdk/commit/60b4b3c4cec89a4f2ed53493ac7b2217b26fb7e0))
* **internal:** grammar fix (it's -> its) ([fa1fb81](https://github.com/miruml/go-server-sdk/commit/fa1fb81de9f7c11a310c6f56b1cfc14e0994dab9))


### Refactors

* **api:** change route to be /beta instead of /v1 ([be825a4](https://github.com/miruml/go-server-sdk/commit/be825a42443bae212e7280f89fdf6c77e2e62a1b))

## 0.0.2 (2025-10-21)

Full Changelog: [v0.0.1...v0.0.2](https://github.com/miruml/go-server-sdk/compare/v0.0.1...v0.0.2)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Or to pin the version:
<!-- x-release-please-start-version -->

```sh
go get -u 'github.com/miruml/go-server-sdk@v0.0.2'
go get -u 'github.com/miruml/go-server-sdk@v0.1.0'
```

<!-- x-release-please-end -->
Expand Down Expand Up @@ -130,7 +130,7 @@ custom := param.Override[miruserver.FooParams](12)

### Request unions

Unions are represented as a struct with fields prefixed by "Of" for each of it's variants,
Unions are represented as a struct with fields prefixed by "Of" for each of its variants,
only one field can be non-zero. The non-zero field will be serialized.

Sub-properties of the union can be accessed via methods on the union struct.
Expand Down
2 changes: 1 addition & 1 deletion deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestDeploymentNewWithOptionalParams(t *testing.T) {
DeviceID: "dvc_123",
NewConfigInstances: []miruserver.DeploymentNewParamsNewConfigInstance{{
ConfigSchemaID: "cfg_sch_123",
Content: map[string]interface{}{
Content: map[string]any{
"direction": "forward",
"speed": 100,
"duration": 10,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/miruml/go-server-sdk
go 1.22

require (
github.com/tidwall/gjson v1.14.4
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
Expand Down
80 changes: 44 additions & 36 deletions internal/apiform/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type encoderField struct {
type encoderEntry struct {
reflect.Type
dateFormat string
arrayFmt string
root bool
}

Expand All @@ -77,6 +78,7 @@ func (e *encoder) typeEncoder(t reflect.Type) encoderFunc {
entry := encoderEntry{
Type: t,
dateFormat: e.dateFormat,
arrayFmt: e.arrayFmt,
root: e.root,
}

Expand Down Expand Up @@ -178,34 +180,9 @@ func (e *encoder) newPrimitiveTypeEncoder(t reflect.Type) encoderFunc {
}
}

func arrayKeyEncoder(arrayFmt string) func(string, int) string {
var keyFn func(string, int) string
switch arrayFmt {
case "comma", "repeat":
keyFn = func(k string, _ int) string { return k }
case "brackets":
keyFn = func(key string, _ int) string { return key + "[]" }
case "indices:dots":
keyFn = func(k string, i int) string {
if k == "" {
return strconv.Itoa(i)
}
return k + "." + strconv.Itoa(i)
}
case "indices:brackets":
keyFn = func(k string, i int) string {
if k == "" {
return strconv.Itoa(i)
}
return k + "[" + strconv.Itoa(i) + "]"
}
}
return keyFn
}

func (e *encoder) newArrayTypeEncoder(t reflect.Type) encoderFunc {
itemEncoder := e.typeEncoder(t.Elem())
keyFn := arrayKeyEncoder(e.arrayFmt)
keyFn := e.arrayKeyEncoder()
return func(key string, v reflect.Value, writer *multipart.Writer) error {
if keyFn == nil {
return fmt.Errorf("apiform: unsupported array format")
Expand Down Expand Up @@ -303,13 +280,10 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc {
})

return func(key string, value reflect.Value, writer *multipart.Writer) error {
if key != "" {
key = key + "."
}

keyFn := e.objKeyEncoder(key)
for _, ef := range encoderFields {
field := value.FieldByIndex(ef.idx)
err := ef.fn(key+ef.tag.name, field, writer)
err := ef.fn(keyFn(ef.tag.name), field, writer)
if err != nil {
return err
}
Expand Down Expand Up @@ -405,6 +379,43 @@ func (e *encoder) newReaderTypeEncoder() encoderFunc {
}
}

func (e encoder) arrayKeyEncoder() func(string, int) string {
var keyFn func(string, int) string
switch e.arrayFmt {
case "comma", "repeat":
keyFn = func(k string, _ int) string { return k }
case "brackets":
keyFn = func(key string, _ int) string { return key + "[]" }
case "indices:dots":
keyFn = func(k string, i int) string {
if k == "" {
return strconv.Itoa(i)
}
return k + "." + strconv.Itoa(i)
}
case "indices:brackets":
keyFn = func(k string, i int) string {
if k == "" {
return strconv.Itoa(i)
}
return k + "[" + strconv.Itoa(i) + "]"
}
}
return keyFn
}

func (e encoder) objKeyEncoder(parent string) func(string) string {
if parent == "" {
return func(child string) string { return child }
}
switch e.arrayFmt {
case "brackets":
return func(child string) string { return parent + "[" + child + "]" }
default:
return func(child string) string { return parent + "." + child }
}
}

// Given a []byte of json (may either be an empty object or an object that already contains entries)
// encode all of the entries in the map to the json byte array.
func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipart.Writer) error {
Expand All @@ -413,10 +424,6 @@ func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipar
value reflect.Value
}

if key != "" {
key = key + "."
}

pairs := []mapPair{}

iter := v.MapRange()
Expand All @@ -434,8 +441,9 @@ func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipar
})

elementEncoder := e.typeEncoder(v.Type().Elem())
keyFn := e.objKeyEncoder(key)
for _, p := range pairs {
err := elementEncoder(key+string(p.key), p.value, writer)
err := elementEncoder(keyFn(p.key), p.value, writer)
if err != nil {
return err
}
Expand Down
51 changes: 50 additions & 1 deletion internal/apiform/form_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ type StructUnion struct {
param.APIUnion
}

type MultipartMarshalerParent struct {
Middle MultipartMarshalerMiddleNext `form:"middle"`
}

type MultipartMarshalerMiddleNext struct {
MiddleNext MultipartMarshalerMiddle `form:"middleNext"`
}

type MultipartMarshalerMiddle struct {
Child int `form:"child"`
}

var tests = map[string]struct {
buf string
val any
Expand Down Expand Up @@ -366,6 +378,19 @@ true
},
},
},
"recursive_struct,brackets": {
`--xxx
Content-Disposition: form-data; name="child[name]"

Alex
--xxx
Content-Disposition: form-data; name="name"

Robert
--xxx--
`,
Recursive{Name: "Robert", Child: &Recursive{Name: "Alex"}},
},

"recursive_struct": {
`--xxx
Expand Down Expand Up @@ -529,6 +554,30 @@ Content-Disposition: form-data; name="union"
Union: UnionTime(time.Date(2010, 05, 23, 0, 0, 0, 0, time.UTC)),
},
},
"deeply-nested-struct,brackets": {
`--xxx
Content-Disposition: form-data; name="middle[middleNext][child]"

10
--xxx--
`,
MultipartMarshalerParent{
Middle: MultipartMarshalerMiddleNext{
MiddleNext: MultipartMarshalerMiddle{
Child: 10,
},
},
},
},
"deeply-nested-map,brackets": {
`--xxx
Content-Disposition: form-data; name="middle[middleNext][child]"

10
--xxx--
`,
map[string]any{"middle": map[string]any{"middleNext": map[string]any{"child": 10}}},
},
}

func TestEncode(t *testing.T) {
Expand All @@ -553,7 +602,7 @@ func TestEncode(t *testing.T) {
}
raw := buf.Bytes()
if string(raw) != strings.ReplaceAll(test.buf, "\n", "\r\n") {
t.Errorf("expected %+#v to serialize to '%s' but got '%s'", test.val, test.buf, string(raw))
t.Errorf("expected %+#v to serialize to '%s' but got '%s' (with format %s)", test.val, test.buf, string(raw), arrayFmt)
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion internal/apijson/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type validatorFunc func(reflect.Value) exactness
var validators sync.Map
var validationRegistry = map[reflect.Type][]validationEntry{}

func RegisterFieldValidator[T any, V string | bool | int](fieldName string, values ...V) {
func RegisterFieldValidator[T any, V string | bool | int | float64](fieldName string, values ...V) {
var t T
parentType := reflect.TypeOf(t)

Expand Down
2 changes: 1 addition & 1 deletion internal/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

package internal

const PackageVersion = "0.0.2" // x-release-please-version
const PackageVersion = "0.1.0" // x-release-please-version
8 changes: 4 additions & 4 deletions option/requestoption.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,28 +263,28 @@ func WithRequestTimeout(dur time.Duration) RequestOption {
// environment to be the "prod" environment. An environment specifies which base URL
// to use by default.
func WithEnvironmentProd() RequestOption {
return requestconfig.WithDefaultBaseURL("https://configs.api.miruml.com/v1/")
return requestconfig.WithDefaultBaseURL("https://configs.api.miruml.com/beta/")
}

// WithEnvironmentUat returns a RequestOption that sets the current
// environment to be the "uat" environment. An environment specifies which base URL
// to use by default.
func WithEnvironmentUat() RequestOption {
return requestconfig.WithDefaultBaseURL("https://uat.api.miruml.com/v1/")
return requestconfig.WithDefaultBaseURL("https://uat.api.miruml.com/beta/")
}

// WithEnvironmentStaging returns a RequestOption that sets the current
// environment to be the "staging" environment. An environment specifies which base URL
// to use by default.
func WithEnvironmentStaging() RequestOption {
return requestconfig.WithDefaultBaseURL("https://configs.dev.api.miruml.com/v1/")
return requestconfig.WithDefaultBaseURL("https://configs.dev.api.miruml.com/beta/")
}

// WithEnvironmentLocal returns a RequestOption that sets the current
// environment to be the "local" environment. An environment specifies which base URL
// to use by default.
func WithEnvironmentLocal() RequestOption {
return requestconfig.WithDefaultBaseURL("http://localhost:8080/v1/")
return requestconfig.WithDefaultBaseURL("http://localhost:8080/beta/")
}

// WithAPIKey returns a RequestOption that sets the client setting "api_key".
Expand Down
2 changes: 1 addition & 1 deletion packages/respjson/respjson.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package respjson
// Use [Field.Valid] to check if an optional value was null or omitted.
//
// A Field will always occur in the following structure, where it
// mirrors the original field in it's parent struct:
// mirrors the original field in its parent struct:
//
// type ExampleObject struct {
// Foo bool `json:"foo"`
Expand Down
1 change: 1 addition & 0 deletions usage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestUsage(t *testing.T) {
option.WithBaseURL(baseURL),
option.WithAPIKey("My API Key"),
)
t.Skip("Prism tests are disabled")
configInstance, err := client.ConfigInstances.Get(
context.TODO(),
"cfg_inst_123",
Expand Down