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 a2e8f7c..1ff3d8b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 13 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/the-san-francisco-compute-company%2Fsfc-nodes-92d29637c0ceda06c752ad70b078ff8dad505843c16b923130774376890baaa5.yml -openapi_spec_hash: 1f20f7d76aa5575ee0601ece537af956 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/the-san-francisco-compute-company%2Fsfc-nodes-899f22ed979e4df6977752fc5de114b92954d5f7499c032fc6aaa58fabd49862.yml +openapi_spec_hash: 95300d446039582ec24c2997bf975ad2 config_hash: cf202573c712b5d91a4d496f35f0ff57 diff --git a/CHANGELOG.md b/CHANGELOG.md index 783e72f..9610c9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.1.0-alpha.4 (2025-11-07) + +Full Changelog: [v0.1.0-alpha.3...v0.1.0-alpha.4](https://github.com/sfcompute/nodes-go/compare/v0.1.0-alpha.3...v0.1.0-alpha.4) + +### Features + +* **api:** api update ([1342c39](https://github.com/sfcompute/nodes-go/commit/1342c3989b487bd7725ab95ba720c7da200c6899)) +* **api:** api update ([02e6349](https://github.com/sfcompute/nodes-go/commit/02e6349b1823ed96fecf3353231e32b00ef3989b)) +* **api:** api update ([26ba165](https://github.com/sfcompute/nodes-go/commit/26ba165c4e22a00f69c89f53edf7b0445057594f)) + + +### Chores + +* **internal:** codegen related update ([766d0aa](https://github.com/sfcompute/nodes-go/commit/766d0aaf277f7c0733d15ba33111d5e9519660a8)) +* **internal:** grammar fix (it's -> its) ([1a198ba](https://github.com/sfcompute/nodes-go/commit/1a198bad6aa4689cecfaccec1953310a60a1a47c)) + ## 0.1.0-alpha.3 (2025-10-13) Full Changelog: [v0.1.0-alpha.2...v0.1.0-alpha.3](https://github.com/sfcompute/nodes-go/compare/v0.1.0-alpha.2...v0.1.0-alpha.3) diff --git a/README.md b/README.md index dbad93d..43ac98e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Or to pin the version: ```sh -go get -u 'github.com/sfcompute/nodes-go@v0.1.0-alpha.3' +go get -u 'github.com/sfcompute/nodes-go@v0.1.0-alpha.4' ``` @@ -125,7 +125,7 @@ custom := param.Override[sfcnodes.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. diff --git a/internal/apijson/encoder.go b/internal/apijson/encoder.go index 8358a2f..ab7a3c1 100644 --- a/internal/apijson/encoder.go +++ b/internal/apijson/encoder.go @@ -16,6 +16,10 @@ import ( var encoders sync.Map // map[encoderEntry]encoderFunc +// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have +// special characters that sjson interprets as a path. +var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace + func Marshal(value any) ([]byte, error) { e := &encoder{dateFormat: time.RFC3339} return e.marshal(value) @@ -270,7 +274,7 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc { if encoded == nil { continue } - json, err = sjson.SetRawBytes(json, ef.tag.name, encoded) + json, err = sjson.SetRawBytes(json, EscapeSJSONKey(ef.tag.name), encoded) if err != nil { return nil, err } @@ -348,7 +352,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error) } encodedKeyString = string(encodedKeyBytes) } - encodedKey := []byte(sjsonReplacer.Replace(encodedKeyString)) + encodedKey := []byte(encodedKeyString) pairs = append(pairs, mapPair{key: encodedKey, value: iter.Value()}) } @@ -366,7 +370,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error) if len(encodedValue) == 0 { continue } - json, err = sjson.SetRawBytes(json, string(p.key), encodedValue) + json, err = sjson.SetRawBytes(json, EscapeSJSONKey(string(p.key)), encodedValue) if err != nil { return nil, err } @@ -386,7 +390,3 @@ func (e *encoder) newMapEncoder(_ reflect.Type) encoderFunc { return json, nil } } - -// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have -// special characters that sjson interprets as a path. -var sjsonReplacer *strings.Replacer = strings.NewReplacer(".", "\\.", ":", "\\:", "*", "\\*") diff --git a/internal/apijson/union.go b/internal/apijson/union.go index c961275..08057d4 100644 --- a/internal/apijson/union.go +++ b/internal/apijson/union.go @@ -78,7 +78,7 @@ 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() + discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value() for _, decoder := range discriminatedDecoders { if discriminator == decoder.discriminator { inner := v.FieldByIndex(decoder.field.Index) @@ -162,7 +162,7 @@ func (d *decoderBuilder) newUnionDecoder(t reflect.Type) decoderFunc { } if len(unionEntry.discriminatorKey) != 0 { - discriminatorValue := n.Get(unionEntry.discriminatorKey).Value() + discriminatorValue := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value() if discriminatorValue == variant.DiscriminatorValue { inner := reflect.New(variant.Type).Elem() err := decoder(n, inner, state) 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/node.go b/node.go index 73f0ea9..f6a1b2a 100644 --- a/node.go +++ b/node.go @@ -140,10 +140,12 @@ type CreateNodesRequestParam struct { CloudInitUserData param.Opt[string] `json:"cloud_init_user_data,omitzero" format:"byte"` // Custom image ID to use for the VM instances ImageID param.Opt[string] `json:"image_id,omitzero"` - // Start time as Unix timestamp in seconds Required for reserved nodes + // Start time as Unix timestamp in seconds Optional for reserved nodes. If not + // provided, defaults to now StartAt param.Opt[int64] `json:"start_at,omitzero"` - // Custom node names Names cannot follow the vm\_{alpha_numeric_chars} as this is - // reserved for system-generated IDs Names cannot be numeric strings + // Custom node names Names cannot begin with 'vm*' or 'n*' as this is reserved for + // system-generated IDs Names cannot be numeric strings Names cannot exceed 128 + // characters Names []string `json:"names,omitzero"` // Any of "autoreserved", "reserved". NodeType NodeType `json:"node_type,omitzero"` diff --git a/packages/param/encoder.go b/packages/param/encoder.go index e35ae27..7c3a221 100644 --- a/packages/param/encoder.go +++ b/packages/param/encoder.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "time" shimjson "github.com/sfcompute/nodes-go/internal/encoding/json" @@ -14,6 +15,10 @@ import ( // EncodedAsDate is not be stable and shouldn't be relied upon type EncodedAsDate Opt[time.Time] +// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have +// special characters that sjson interprets as a path. +var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace + type forceOmit int func (m EncodedAsDate) MarshalJSON() ([]byte, error) { @@ -52,7 +57,7 @@ func MarshalWithExtras[T ParamStruct, R any](f T, underlying any, extras map[str } continue } - bytes, err = sjson.SetBytes(bytes, k, v) + bytes, err = sjson.SetBytes(bytes, EscapeSJSONKey(k), v) if err != nil { return nil, err } diff --git a/packages/respjson/respjson.go b/packages/respjson/respjson.go index cc0088c..9e61c5c 100644 --- a/packages/respjson/respjson.go +++ b/packages/respjson/respjson.go @@ -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"` diff --git a/vm.go b/vm.go index 675fc64..ca30cf2 100644 --- a/vm.go +++ b/vm.go @@ -97,16 +97,22 @@ func (r *VMLogsResponseData) UnmarshalJSON(data []byte) error { } type VmsshResponse struct { - SSHHostname string `json:"ssh_hostname,required"` - SSHPort int64 `json:"ssh_port,required"` - SSHHostKeys []VmsshResponseSSHHostKey `json:"ssh_host_keys,nullable"` + SSHHostname string `json:"ssh_hostname,required"` + SSHPort int64 `json:"ssh_port,required"` + // Unix timestamp in seconds since epoch + LastAttemptedKeyUpdate int64 `json:"last_attempted_key_update,nullable"` + // Unix timestamp in seconds since epoch + LastSuccessfulKeyUpdate int64 `json:"last_successful_key_update,nullable"` + SSHHostKeys []VmsshResponseSSHHostKey `json:"ssh_host_keys,nullable"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { - SSHHostname respjson.Field - SSHPort respjson.Field - SSHHostKeys respjson.Field - ExtraFields map[string]respjson.Field - raw string + SSHHostname respjson.Field + SSHPort respjson.Field + LastAttemptedKeyUpdate respjson.Field + LastSuccessfulKeyUpdate respjson.Field + SSHHostKeys respjson.Field + ExtraFields map[string]respjson.Field + raw string } `json:"-"` } @@ -117,7 +123,7 @@ func (r *VmsshResponse) UnmarshalJSON(data []byte) error { } type VmsshResponseSSHHostKey struct { - Base64EncodedKey string `json:"base64_encoded_key,required"` + Base64EncodedKey string `json:"base64_encoded_key,required" format:"byte"` KeyType string `json:"key_type,required"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { diff --git a/vmimage.go b/vmimage.go index 90fcc19..d821cae 100644 --- a/vmimage.go +++ b/vmimage.go @@ -127,6 +127,8 @@ type VMImageGetResponse struct { Name string `json:"name,required"` // Any of "image". Object VMImageGetResponseObject `json:"object,required"` + // Size of the image file in bytes + ObjectSize int64 `json:"object_size,required"` // SHA256 hash of the image file for integrity verification Sha256Hash string `json:"sha256_hash,required"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. @@ -136,6 +138,7 @@ type VMImageGetResponse struct { ImageID respjson.Field Name respjson.Field Object respjson.Field + ObjectSize respjson.Field Sha256Hash respjson.Field ExtraFields map[string]respjson.Field raw string