diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ab58c19..9ad4bf8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.141.0" + ".": "0.142.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index ea0d527..4725ed9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 176 +configured_endpoints: 178 diff --git a/CHANGELOG.md b/CHANGELOG.md index e3030bc..732e255 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.142.0](https://github.com/kernel/kernel-go-sdk/compare/v0.141.0...v0.142.0) (2026-11-06) + + +### Features + +* Let reserved orgs list, pin and move ISP proxy IPs ([cc6cd8e](https://github.com/kernel/kernel-go-sdk/commit/cc6cd8ecff93dbb826e7fb7887f3cd3642631366)) + ## [0.141.0](https://github.com/kernel/kernel-go-sdk/compare/v0.139.0...v0.141.0) (2026-11-05) diff --git a/README.md b/README.md index 3dd397d..a7620b4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Or to pin the version: ```sh -go get -u 'github.com/kernel/kernel-go-sdk@v0.141.0' +go get -u 'github.com/kernel/kernel-go-sdk@v0.142.0' ``` diff --git a/api.md b/api.md index 6d37f54..a12723e 100644 --- a/api.md +++ b/api.md @@ -479,6 +479,18 @@ Methods: - client.Proxies.Delete(ctx context.Context, idOrName string) error - client.Proxies.Check(ctx context.Context, idOrName string, body kernel.ProxyCheckParams) (\*kernel.ProxyCheckResponse, error) +# ProxyIPs + +Response Types: + +- kernel.ProxyIP +- kernel.ProxyIPDetail + +Methods: + +- client.ProxyIPs.Get(ctx context.Context, ipAddress string) (\*kernel.ProxyIPDetail, error) +- client.ProxyIPs.List(ctx context.Context, query kernel.ProxyIPListParams) (\*pagination.OffsetPagination[kernel.ProxyIP], error) + # Extensions Response Types: diff --git a/client.go b/client.go index 13c6264..b3a0f98 100644 --- a/client.go +++ b/client.go @@ -36,6 +36,8 @@ type Client struct { Telemetry TelemetryService // Create and manage proxy configurations for routing browser traffic. Proxies ProxyService + // Create and manage proxy configurations for routing browser traffic. + ProxyIPs ProxyIPService // Create, list, retrieve, and delete browser extensions. Extensions ExtensionService // Create and manage browser pools for acquiring and releasing browsers. @@ -101,6 +103,7 @@ func NewClient(opts ...option.RequestOption) (r Client) { r.Auth = NewAuthService(opts...) r.Telemetry = NewTelemetryService(opts...) r.Proxies = NewProxyService(opts...) + r.ProxyIPs = NewProxyIPService(opts...) r.Extensions = NewExtensionService(opts...) r.BrowserPools = NewBrowserPoolService(opts...) r.VaultProviderConfigs = NewVaultProviderConfigService(opts...) diff --git a/configregistry.go b/configregistry.go index 589c791..a822e8d 100644 --- a/configregistry.go +++ b/configregistry.go @@ -507,6 +507,8 @@ func (r *ProxyManagedCreate) UnmarshalJSON(data []byte) error { // Use the methods beginning with 'As' to cast the union to one of its variants. type ProxyManagedCreateConfigUnion struct { Country string `json:"country"` + // This field is from variant [ProxyManagedCreateConfigIsp]. + IPAddress string `json:"ip_address"` // This field is from variant [ProxyManagedCreateConfigResidential]. Asn string `json:"asn"` City string `json:"city"` @@ -526,18 +528,19 @@ type ProxyManagedCreateConfigUnion struct { // This field is from variant [ProxyManagedCreateConfigCustom]. Username string `json:"username"` JSON struct { - Country respjson.Field - Asn respjson.Field - City respjson.Field - Os respjson.Field - State respjson.Field - Zip respjson.Field - Host respjson.Field - Port respjson.Field - CaBundle respjson.Field - Password respjson.Field - Username respjson.Field - raw string + Country respjson.Field + IPAddress respjson.Field + Asn respjson.Field + City respjson.Field + Os respjson.Field + State respjson.Field + Zip respjson.Field + Host respjson.Field + Port respjson.Field + CaBundle respjson.Field + Password respjson.Field + Username respjson.Field + raw string } `json:"-"` } @@ -571,11 +574,21 @@ func (r *ProxyManagedCreateConfigUnion) UnmarshalJSON(data []byte) error { // Configuration for an ISP proxy. type ProxyManagedCreateConfigIsp struct { // ISO 3166 country code. Supported countries are US, GB, FR, DE, SG, and KR. - // Defaults to US if not provided. + // Defaults to US if not provided, or to the country of ip_address when that is + // set. Country string `json:"country"` + // Reserved exit IP to pin the proxy to. Available only to organizations with a + // reserved ISP allocation; the IP must be active in it (see GET /proxy_ips) and, + // when country is also set, in that country. Omit to let Kernel choose an IP. A + // proxy read includes it only when the proxy's primary exit IP is one of the + // organization's reserved IPs; every ISP proxy's exit IP is on the top-level + // ip_address. An IP outside the allocation is rejected with code ip_not_reserved; + // one that cannot currently be assigned, with 409 ip_unavailable. + IPAddress string `json:"ip_address"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Country respjson.Field + IPAddress respjson.Field ExtraFields map[string]respjson.Field raw string } `json:"-"` diff --git a/internal/version.go b/internal/version.go index 16231fb..85f1142 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.141.0" // x-release-please-version +const PackageVersion = "0.142.0" // x-release-please-version diff --git a/proxy.go b/proxy.go index e4ef8e6..cfa5892 100644 --- a/proxy.go +++ b/proxy.go @@ -65,8 +65,13 @@ func (r *ProxyService) Get(ctx context.Context, idOrName string, opts ...option. return res, err } -// Update a proxy's name. Proxy names are not unique and duplicate names are -// allowed. +// Update a proxy's name, or move an ISP proxy to another reserved exit IP. Proxy +// names are not unique and duplicate names are allowed. Moving requires a reserved +// ISP allocation and is accepted only for proxies of type isp: the new endpoint is +// probed first and nothing changes if the probe fails. An IP outside the +// allocation is a 400 with code ip_not_reserved. A 409 means the IP cannot +// currently be assigned or the proxy was changed concurrently; read it back and +// retry. func (r *ProxyService) Update(ctx context.Context, idOrName string, body ProxyUpdateParams, opts ...option.RequestOption) (res *ProxyUpdateResponse, err error) { opts = slices.Concat(r.Options, opts) if idOrName == "" { @@ -195,6 +200,8 @@ const ( // Use the methods beginning with 'As' to cast the union to one of its variants. type ProxyNewResponseConfigUnion struct { Country string `json:"country"` + // This field is from variant [ProxyNewResponseConfigIsp]. + IPAddress string `json:"ip_address"` // This field is from variant [ProxyNewResponseConfigResidential]. Asn string `json:"asn"` City string `json:"city"` @@ -215,6 +222,7 @@ type ProxyNewResponseConfigUnion struct { Username string `json:"username"` JSON struct { Country respjson.Field + IPAddress respjson.Field Asn respjson.Field City respjson.Field Os respjson.Field @@ -259,11 +267,21 @@ func (r *ProxyNewResponseConfigUnion) UnmarshalJSON(data []byte) error { // Configuration for an ISP proxy. type ProxyNewResponseConfigIsp struct { // ISO 3166 country code. Supported countries are US, GB, FR, DE, SG, and KR. - // Defaults to US if not provided. + // Defaults to US if not provided, or to the country of ip_address when that is + // set. Country string `json:"country"` + // Reserved exit IP to pin the proxy to. Available only to organizations with a + // reserved ISP allocation; the IP must be active in it (see GET /proxy_ips) and, + // when country is also set, in that country. Omit to let Kernel choose an IP. A + // proxy read includes it only when the proxy's primary exit IP is one of the + // organization's reserved IPs; every ISP proxy's exit IP is on the top-level + // ip_address. An IP outside the allocation is rejected with code ip_not_reserved; + // one that cannot currently be assigned, with 409 ip_unavailable. + IPAddress string `json:"ip_address"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Country respjson.Field + IPAddress respjson.Field ExtraFields map[string]respjson.Field raw string } `json:"-"` @@ -446,6 +464,8 @@ const ( // Use the methods beginning with 'As' to cast the union to one of its variants. type ProxyGetResponseConfigUnion struct { Country string `json:"country"` + // This field is from variant [ProxyGetResponseConfigIsp]. + IPAddress string `json:"ip_address"` // This field is from variant [ProxyGetResponseConfigResidential]. Asn string `json:"asn"` City string `json:"city"` @@ -466,6 +486,7 @@ type ProxyGetResponseConfigUnion struct { Username string `json:"username"` JSON struct { Country respjson.Field + IPAddress respjson.Field Asn respjson.Field City respjson.Field Os respjson.Field @@ -510,11 +531,21 @@ func (r *ProxyGetResponseConfigUnion) UnmarshalJSON(data []byte) error { // Configuration for an ISP proxy. type ProxyGetResponseConfigIsp struct { // ISO 3166 country code. Supported countries are US, GB, FR, DE, SG, and KR. - // Defaults to US if not provided. + // Defaults to US if not provided, or to the country of ip_address when that is + // set. Country string `json:"country"` + // Reserved exit IP to pin the proxy to. Available only to organizations with a + // reserved ISP allocation; the IP must be active in it (see GET /proxy_ips) and, + // when country is also set, in that country. Omit to let Kernel choose an IP. A + // proxy read includes it only when the proxy's primary exit IP is one of the + // organization's reserved IPs; every ISP proxy's exit IP is on the top-level + // ip_address. An IP outside the allocation is rejected with code ip_not_reserved; + // one that cannot currently be assigned, with 409 ip_unavailable. + IPAddress string `json:"ip_address"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Country respjson.Field + IPAddress respjson.Field ExtraFields map[string]respjson.Field raw string } `json:"-"` @@ -697,6 +728,8 @@ const ( // Use the methods beginning with 'As' to cast the union to one of its variants. type ProxyUpdateResponseConfigUnion struct { Country string `json:"country"` + // This field is from variant [ProxyUpdateResponseConfigIsp]. + IPAddress string `json:"ip_address"` // This field is from variant [ProxyUpdateResponseConfigResidential]. Asn string `json:"asn"` City string `json:"city"` @@ -717,6 +750,7 @@ type ProxyUpdateResponseConfigUnion struct { Username string `json:"username"` JSON struct { Country respjson.Field + IPAddress respjson.Field Asn respjson.Field City respjson.Field Os respjson.Field @@ -761,11 +795,21 @@ func (r *ProxyUpdateResponseConfigUnion) UnmarshalJSON(data []byte) error { // Configuration for an ISP proxy. type ProxyUpdateResponseConfigIsp struct { // ISO 3166 country code. Supported countries are US, GB, FR, DE, SG, and KR. - // Defaults to US if not provided. + // Defaults to US if not provided, or to the country of ip_address when that is + // set. Country string `json:"country"` + // Reserved exit IP to pin the proxy to. Available only to organizations with a + // reserved ISP allocation; the IP must be active in it (see GET /proxy_ips) and, + // when country is also set, in that country. Omit to let Kernel choose an IP. A + // proxy read includes it only when the proxy's primary exit IP is one of the + // organization's reserved IPs; every ISP proxy's exit IP is on the top-level + // ip_address. An IP outside the allocation is rejected with code ip_not_reserved; + // one that cannot currently be assigned, with 409 ip_unavailable. + IPAddress string `json:"ip_address"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Country respjson.Field + IPAddress respjson.Field ExtraFields map[string]respjson.Field raw string } `json:"-"` @@ -948,6 +992,8 @@ const ( // Use the methods beginning with 'As' to cast the union to one of its variants. type ProxyListResponseConfigUnion struct { Country string `json:"country"` + // This field is from variant [ProxyListResponseConfigIsp]. + IPAddress string `json:"ip_address"` // This field is from variant [ProxyListResponseConfigResidential]. Asn string `json:"asn"` City string `json:"city"` @@ -968,6 +1014,7 @@ type ProxyListResponseConfigUnion struct { Username string `json:"username"` JSON struct { Country respjson.Field + IPAddress respjson.Field Asn respjson.Field City respjson.Field Os respjson.Field @@ -1012,11 +1059,21 @@ func (r *ProxyListResponseConfigUnion) UnmarshalJSON(data []byte) error { // Configuration for an ISP proxy. type ProxyListResponseConfigIsp struct { // ISO 3166 country code. Supported countries are US, GB, FR, DE, SG, and KR. - // Defaults to US if not provided. + // Defaults to US if not provided, or to the country of ip_address when that is + // set. Country string `json:"country"` + // Reserved exit IP to pin the proxy to. Available only to organizations with a + // reserved ISP allocation; the IP must be active in it (see GET /proxy_ips) and, + // when country is also set, in that country. Omit to let Kernel choose an IP. A + // proxy read includes it only when the proxy's primary exit IP is one of the + // organization's reserved IPs; every ISP proxy's exit IP is on the top-level + // ip_address. An IP outside the allocation is rejected with code ip_not_reserved; + // one that cannot currently be assigned, with 409 ip_unavailable. + IPAddress string `json:"ip_address"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Country respjson.Field + IPAddress respjson.Field ExtraFields map[string]respjson.Field raw string } `json:"-"` @@ -1199,6 +1256,8 @@ const ( // Use the methods beginning with 'As' to cast the union to one of its variants. type ProxyCheckResponseConfigUnion struct { Country string `json:"country"` + // This field is from variant [ProxyCheckResponseConfigIsp]. + IPAddress string `json:"ip_address"` // This field is from variant [ProxyCheckResponseConfigResidential]. Asn string `json:"asn"` City string `json:"city"` @@ -1219,6 +1278,7 @@ type ProxyCheckResponseConfigUnion struct { Username string `json:"username"` JSON struct { Country respjson.Field + IPAddress respjson.Field Asn respjson.Field City respjson.Field Os respjson.Field @@ -1263,11 +1323,21 @@ func (r *ProxyCheckResponseConfigUnion) UnmarshalJSON(data []byte) error { // Configuration for an ISP proxy. type ProxyCheckResponseConfigIsp struct { // ISO 3166 country code. Supported countries are US, GB, FR, DE, SG, and KR. - // Defaults to US if not provided. + // Defaults to US if not provided, or to the country of ip_address when that is + // set. Country string `json:"country"` + // Reserved exit IP to pin the proxy to. Available only to organizations with a + // reserved ISP allocation; the IP must be active in it (see GET /proxy_ips) and, + // when country is also set, in that country. Omit to let Kernel choose an IP. A + // proxy read includes it only when the proxy's primary exit IP is one of the + // organization's reserved IPs; every ISP proxy's exit IP is on the top-level + // ip_address. An IP outside the allocation is rejected with code ip_not_reserved; + // one that cannot currently be assigned, with 409 ip_unavailable. + IPAddress string `json:"ip_address"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Country respjson.Field + IPAddress respjson.Field ExtraFields map[string]respjson.Field raw string } `json:"-"` @@ -1453,6 +1523,14 @@ func (u *ProxyNewParamsConfigUnion) asAny() any { return nil } +// Returns a pointer to the underlying variant's property, if present. +func (u ProxyNewParamsConfigUnion) GetIPAddress() *string { + if vt := u.OfIsp; vt != nil && vt.IPAddress.Valid() { + return &vt.IPAddress.Value + } + return nil +} + // Returns a pointer to the underlying variant's property, if present. func (u ProxyNewParamsConfigUnion) GetAsn() *string { if vt := u.OfResidential; vt != nil && vt.Asn.Valid() { @@ -1552,8 +1630,17 @@ func (u ProxyNewParamsConfigUnion) GetState() *string { // Configuration for an ISP proxy. type ProxyNewParamsConfigIsp struct { // ISO 3166 country code. Supported countries are US, GB, FR, DE, SG, and KR. - // Defaults to US if not provided. + // Defaults to US if not provided, or to the country of ip_address when that is + // set. Country param.Opt[string] `json:"country,omitzero"` + // Reserved exit IP to pin the proxy to. Available only to organizations with a + // reserved ISP allocation; the IP must be active in it (see GET /proxy_ips) and, + // when country is also set, in that country. Omit to let Kernel choose an IP. A + // proxy read includes it only when the proxy's primary exit IP is one of the + // organization's reserved IPs; every ISP proxy's exit IP is on the top-level + // ip_address. An IP outside the allocation is rejected with code ip_not_reserved; + // one that cannot currently be assigned, with 409 ip_unavailable. + IPAddress param.Opt[string] `json:"ip_address,omitzero"` paramObj } @@ -1660,7 +1747,9 @@ const ( type ProxyUpdateParams struct { // New proxy name. Proxy names are trimmed and length-checked only; duplicates are // allowed because proxies are updated by ID, not by name. - Name string `json:"name" api:"required"` + Name param.Opt[string] `json:"name,omitzero"` + // Changes to an ISP proxy's exit IP. Accepted only for proxies of type `isp`. + Config ProxyUpdateParamsConfig `json:"config,omitzero"` paramObj } @@ -1672,7 +1761,32 @@ func (r *ProxyUpdateParams) UnmarshalJSON(data []byte) error { return apijson.UnmarshalRoot(data, r) } +// Changes to an ISP proxy's exit IP. Accepted only for proxies of type `isp`. +// +// The property IPAddress is required. +type ProxyUpdateParamsConfig struct { + // Reserved exit IP to move the proxy to. The IP must be active in the + // organization's allocation and in the proxy's country. The proxy keeps its ID. + // The new endpoint is probed before anything changes; a failed probe leaves the + // proxy as it was. Live browsers open new connections on the new IP within a few + // seconds; established connections drain on the old one. + IPAddress string `json:"ip_address" api:"required"` + paramObj +} + +func (r ProxyUpdateParamsConfig) MarshalJSON() (data []byte, err error) { + type shadow ProxyUpdateParamsConfig + return param.MarshalObject(r, (*shadow)(&r)) +} +func (r *ProxyUpdateParamsConfig) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type ProxyListParams struct { + // Exact match on the primary exit IP of ISP proxies. Must be an IPv4 address. Only + // ISP proxies are matched: the ip_address shown on other proxy types is a one-time + // observation, not a stable identity. + IPAddress param.Opt[string] `query:"ip_address,omitzero" json:"-"` // Limit the number of proxies to return. Limit param.Opt[int64] `query:"limit,omitzero" json:"-"` // Exact-match filter on proxy name using the database collation. In production, diff --git a/proxy_test.go b/proxy_test.go index 50156d0..e10f4ff 100644 --- a/proxy_test.go +++ b/proxy_test.go @@ -31,7 +31,8 @@ func TestProxyNewWithOptionalParams(t *testing.T) { BypassHosts: []string{"string"}, Config: kernel.ProxyNewParamsConfigUnion{ OfIsp: &kernel.ProxyNewParamsConfigIsp{ - Country: kernel.String("US"), + Country: kernel.String("US"), + IPAddress: kernel.String("203.0.113.10"), }, }, Name: kernel.String("name"), @@ -69,7 +70,7 @@ func TestProxyGet(t *testing.T) { } } -func TestProxyUpdate(t *testing.T) { +func TestProxyUpdateWithOptionalParams(t *testing.T) { t.Skip("Mock server tests are disabled") baseURL := "http://localhost:4010" if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { @@ -86,7 +87,10 @@ func TestProxyUpdate(t *testing.T) { context.TODO(), "id_or_name", kernel.ProxyUpdateParams{ - Name: "my-renamed-proxy", + Config: kernel.ProxyUpdateParamsConfig{ + IPAddress: "203.0.113.10", + }, + Name: kernel.String("my-renamed-proxy"), }, ) if err != nil { @@ -112,10 +116,11 @@ func TestProxyListWithOptionalParams(t *testing.T) { option.WithAPIKey("My API Key"), ) _, err := client.Proxies.List(context.TODO(), kernel.ProxyListParams{ - Limit: kernel.Int(1), - Name: kernel.String("name"), - Offset: kernel.Int(0), - Query: kernel.String("query"), + IPAddress: kernel.String("ip_address"), + Limit: kernel.Int(1), + Name: kernel.String("name"), + Offset: kernel.Int(0), + Query: kernel.String("query"), }) if err != nil { var apierr *kernel.Error diff --git a/proxyip.go b/proxyip.go new file mode 100644 index 0000000..fb81b75 --- /dev/null +++ b/proxyip.go @@ -0,0 +1,224 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/url" + "slices" + + "github.com/kernel/kernel-go-sdk/internal/apijson" + "github.com/kernel/kernel-go-sdk/internal/apiquery" + "github.com/kernel/kernel-go-sdk/internal/requestconfig" + "github.com/kernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk/packages/pagination" + "github.com/kernel/kernel-go-sdk/packages/param" + "github.com/kernel/kernel-go-sdk/packages/respjson" +) + +// Create and manage proxy configurations for routing browser traffic. +// +// ProxyIPService contains methods and other services that help with interacting +// with the kernel API. +// +// Note, unlike clients, this service does not read variables from the environment +// automatically. You should not instantiate this service directly, and instead use +// the [NewProxyIPService] method instead. +type ProxyIPService struct { + Options []option.RequestOption +} + +// NewProxyIPService generates a new service that applies the given options to each +// request. These options are applied after the parent client's options (if there +// is one), and before any request-specific options. +func NewProxyIPService(opts ...option.RequestOption) (r ProxyIPService) { + r = ProxyIPService{} + r.Options = opts + return +} + +// Retrieve one reserved IP with the ISP proxies using it as their primary exit. +// proxy_count spans the organization; proxies lists only those in the current +// project. Returns 404 when the IP is not in the organization's allocation and 403 +// with code reserved_isp_not_enabled when the organization has none. Scoped API +// keys require proxies:read on the whole project or organization, not on +// individual proxies; otherwise the operation returns 403 with code +// insufficient_scope. +func (r *ProxyIPService) Get(ctx context.Context, ipAddress string, opts ...option.RequestOption) (res *ProxyIPDetail, err error) { + opts = slices.Concat(r.Options, opts) + if ipAddress == "" { + err = errors.New("missing required ip_address parameter") + return nil, err + } + path := fmt.Sprintf("proxy_ips/%s", ipAddress) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return res, err +} + +// List the IP addresses reserved for the organization's ISP proxies, with each +// IP's country and whether it can currently be assigned. Available only to +// organizations with a reserved ISP allocation; others receive a 403 with code +// reserved_isp_not_enabled. Served from the allocation itself, so IPs appear +// whether or not a proxy uses them. Scoped API keys require proxies:read on the +// whole project or organization, not on individual proxies; otherwise the +// operation returns 403 with code insufficient_scope. +func (r *ProxyIPService) List(ctx context.Context, query ProxyIPListParams, opts ...option.RequestOption) (res *pagination.OffsetPagination[ProxyIP], err error) { + var raw *http.Response + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) + path := "proxy_ips" + 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 the IP addresses reserved for the organization's ISP proxies, with each +// IP's country and whether it can currently be assigned. Available only to +// organizations with a reserved ISP allocation; others receive a 403 with code +// reserved_isp_not_enabled. Served from the allocation itself, so IPs appear +// whether or not a proxy uses them. Scoped API keys require proxies:read on the +// whole project or organization, not on individual proxies; otherwise the +// operation returns 403 with code insufficient_scope. +func (r *ProxyIPService) ListAutoPaging(ctx context.Context, query ProxyIPListParams, opts ...option.RequestOption) *pagination.OffsetPaginationAutoPager[ProxyIP] { + return pagination.NewOffsetPaginationAutoPager(r.List(ctx, query, opts...)) +} + +// One IP address reserved for the organization's ISP proxies. +type ProxyIP struct { + // ISO 3166 country code of the IP. + Country string `json:"country" api:"required"` + // Reserved exit IP address. + IPAddress string `json:"ip_address" api:"required"` + // Whether the IP can currently be assigned. An excluded IP is listed but cannot be + // pinned or moved to; proxies already on it keep working. + // + // Any of "active", "excluded". + Status ProxyIPStatus `json:"status" api:"required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Country respjson.Field + IPAddress respjson.Field + Status respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r ProxyIP) RawJSON() string { return r.JSON.raw } +func (r *ProxyIP) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Whether the IP can currently be assigned. An excluded IP is listed but cannot be +// pinned or moved to; proxies already on it keep working. +type ProxyIPStatus string + +const ( + ProxyIPStatusActive ProxyIPStatus = "active" + ProxyIPStatusExcluded ProxyIPStatus = "excluded" +) + +// A reserved IP with the ISP proxies using it as their primary exit. Reservations +// are organization-wide, so proxy_count covers every project while proxies lists +// only those in the current project. +type ProxyIPDetail struct { + // ISO 3166 country code of the IP. + Country string `json:"country" api:"required"` + // Reserved exit IP address. + IPAddress string `json:"ip_address" api:"required"` + // ISP proxies in the current project whose primary exit IP is this address. + Proxies []ProxyIPDetailProxy `json:"proxies" api:"required"` + // Number of ISP proxies across the organization whose primary exit IP is this + // address. + ProxyCount int64 `json:"proxy_count" api:"required"` + // Whether the IP can currently be assigned. An excluded IP is listed but cannot be + // pinned or moved to; proxies already on it keep working. + // + // Any of "active", "excluded". + Status ProxyIPDetailStatus `json:"status" api:"required"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Country respjson.Field + IPAddress respjson.Field + Proxies respjson.Field + ProxyCount respjson.Field + Status respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r ProxyIPDetail) RawJSON() string { return r.JSON.raw } +func (r *ProxyIPDetail) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// An ISP proxy whose primary exit is the reserved IP. +type ProxyIPDetailProxy struct { + ID string `json:"id" api:"required"` + Name string `json:"name"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + ID respjson.Field + Name respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r ProxyIPDetailProxy) RawJSON() string { return r.JSON.raw } +func (r *ProxyIPDetailProxy) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Whether the IP can currently be assigned. An excluded IP is listed but cannot be +// pinned or moved to; proxies already on it keep working. +type ProxyIPDetailStatus string + +const ( + ProxyIPDetailStatusActive ProxyIPDetailStatus = "active" + ProxyIPDetailStatusExcluded ProxyIPDetailStatus = "excluded" +) + +type ProxyIPListParams struct { + // ISO 3166 country code to filter by. Must be a country ISP proxies support. + Country param.Opt[string] `query:"country,omitzero" json:"-"` + // Limit the number of IPs to return. + Limit param.Opt[int64] `query:"limit,omitzero" json:"-"` + // Offset the number of IPs to return. + Offset param.Opt[int64] `query:"offset,omitzero" json:"-"` + // Filter by whether the IP can currently be assigned. + // + // Any of "active", "excluded". + Status ProxyIPListParamsStatus `query:"status,omitzero" json:"-"` + paramObj +} + +// URLQuery serializes [ProxyIPListParams]'s query parameters as `url.Values`. +func (r ProxyIPListParams) URLQuery() (v url.Values, err error) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatComma, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +// Filter by whether the IP can currently be assigned. +type ProxyIPListParamsStatus string + +const ( + ProxyIPListParamsStatusActive ProxyIPListParamsStatus = "active" + ProxyIPListParamsStatusExcluded ProxyIPListParamsStatus = "excluded" +) diff --git a/proxyip_test.go b/proxyip_test.go new file mode 100644 index 0000000..392622b --- /dev/null +++ b/proxyip_test.go @@ -0,0 +1,65 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package kernel_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/internal/testutil" + "github.com/kernel/kernel-go-sdk/option" +) + +func TestProxyIPGet(t *testing.T) { + t.Skip("Mock server tests are disabled") + 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.ProxyIPs.Get(context.TODO(), "ip_address") + 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()) + } +} + +func TestProxyIPListWithOptionalParams(t *testing.T) { + t.Skip("Mock server tests are disabled") + 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.ProxyIPs.List(context.TODO(), kernel.ProxyIPListParams{ + Country: kernel.String("country"), + Limit: kernel.Int(1), + Offset: kernel.Int(0), + Status: kernel.ProxyIPListParamsStatusActive, + }) + 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/shared/shared.go b/shared/shared.go index 1d7ae0f..9c227d6 100644 --- a/shared/shared.go +++ b/shared/shared.go @@ -131,9 +131,11 @@ func (r *BrowserProfileParam) UnmarshalJSON(data []byte) error { // determined based on the resolution (higher resolutions use lower refresh rates // to keep bandwidth reasonable). type BrowserViewport struct { - // Browser window height in pixels. Any positive integer is accepted. + // Browser window height in pixels. Any positive integer is accepted; headful + // browsers require at least 64. Height int64 `json:"height" api:"required"` - // Browser window width in pixels. Any positive integer is accepted. + // Browser window width in pixels. Any positive integer is accepted; headful + // browsers require at least 64. Width int64 `json:"width" api:"required"` // Display refresh rate in Hz. Any positive integer is accepted; if omitted, GPU // browsers default to 60 and other browsers derive it from width and height. @@ -178,9 +180,11 @@ func (r BrowserViewport) ToParam() BrowserViewportParam { // // The properties Height, Width are required. type BrowserViewportParam struct { - // Browser window height in pixels. Any positive integer is accepted. + // Browser window height in pixels. Any positive integer is accepted; headful + // browsers require at least 64. Height int64 `json:"height" api:"required"` - // Browser window width in pixels. Any positive integer is accepted. + // Browser window width in pixels. Any positive integer is accepted; headful + // browsers require at least 64. Width int64 `json:"width" api:"required"` // Display refresh rate in Hz. Any positive integer is accepted; if omitted, GPU // browsers default to 60 and other browsers derive it from width and height.