diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 8587ff4..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM debian:bookworm-slim - -RUN apt-get update && apt-get install -y \ - libxkbcommon0 \ - ca-certificates \ - git \ - unzip \ - libc++1 \ - vim \ - curl \ - procps \ - && apt-get clean autoclean - -RUN curl -OL https://go.dev/dl/go1.24.0.linux-amd64.tar.gz && \ - tar -C /usr/local -xzvf go1.24.0.linux-amd64.tar.gz && \ - rm go1.24.0.linux-amd64.tar.gz -ENV PATH="$PATH:/usr/local/go/bin" - -# Ensure UTF-8 encoding -ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d55fc4d..889ae34 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,20 +1,7 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/debian { - "name": "Debian", - "build": { - "dockerfile": "Dockerfile" - } - - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Configure tool-specific properties. - // "customizations": {}, - - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" + "name": "Development", + "image": "mcr.microsoft.com/devcontainers/go:1.23-bookworm", + "postCreateCommand": "go mod tidy" } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 901e734..f7a52ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,18 @@ name: CI on: push: - branches: - - main - pull_request: - branches: - - main - - next + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' jobs: lint: + timeout-minutes: 10 name: lint - runs-on: ubuntu-latest - + runs-on: ${{ github.repository == 'stainless-sdks/gitpod-go' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@v4 @@ -25,9 +25,9 @@ jobs: - name: Run lints run: ./scripts/lint test: + timeout-minutes: 10 name: test - runs-on: ubuntu-latest - + runs-on: ${{ github.repository == 'stainless-sdks/gitpod-go' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@v4 @@ -41,4 +41,3 @@ jobs: - name: Run tests run: ./scripts/test - diff --git a/.release-please-manifest.json b/.release-please-manifest.json index da59f99..2aca35a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.4.0" + ".": "0.5.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2c4d8ac..d375176 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,4 @@ -configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-3655d5ad0ac3e228c1519af70dbf3d0bfa3c47a2d06d4cac92a650da051b49a6.yml +configured_endpoints: 119 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-ca9a49ac7fbb63f55611fd7cd48a22a3ff8b38a797125c8513e891d9b7345550.yml +openapi_spec_hash: fd6ffbdfaefcc555e61ca1c565e05214 +config_hash: bb9d0a0bdadbee0985dd7c1e4f0e9e8a diff --git a/CHANGELOG.md b/CHANGELOG.md index 44d39ab..853c2d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,52 @@ # Changelog +## 0.5.0 (2025-06-06) + +Full Changelog: [v0.4.0...v0.5.0](https://github.com/gitpod-io/gitpod-sdk-go/compare/v0.4.0...v0.5.0) + +### Features + +* **api:** manual updates ([04616ff](https://github.com/gitpod-io/gitpod-sdk-go/commit/04616fff8d30f184d182f2f446c9d91ee82b896b)) +* **api:** manual updates ([ccca7e5](https://github.com/gitpod-io/gitpod-sdk-go/commit/ccca7e5f582b1cb79fa8f18111ac2d59f3c8f42e)) +* **api:** manual updates ([60b3e5e](https://github.com/gitpod-io/gitpod-sdk-go/commit/60b3e5e20df48bbb2e6d544ccbd266bf418eea7c)) +* **api:** manual updates ([3ecd0c4](https://github.com/gitpod-io/gitpod-sdk-go/commit/3ecd0c4894b471a99b52da94d98ee4b1c12cde30)) +* **api:** manual updates ([77f5109](https://github.com/gitpod-io/gitpod-sdk-go/commit/77f510928da90a197e8e556203677aa1a5838b9b)) +* **client:** add support for reading base URL from environment variable ([e074cd4](https://github.com/gitpod-io/gitpod-sdk-go/commit/e074cd4d1977e7d9c861c9a08a5e8fd0b182d944)) +* **client:** allow custom baseurls without trailing slash ([#55](https://github.com/gitpod-io/gitpod-sdk-go/issues/55)) ([20b4808](https://github.com/gitpod-io/gitpod-sdk-go/commit/20b480853025ee9b5402ae5a361010c7de9eabdc)) +* **client:** improve default client options support ([#57](https://github.com/gitpod-io/gitpod-sdk-go/issues/57)) ([0cb0d29](https://github.com/gitpod-io/gitpod-sdk-go/commit/0cb0d293a64defd394978cd2b4da6cb006753a69)) +* **client:** support custom http clients ([#65](https://github.com/gitpod-io/gitpod-sdk-go/issues/65)) ([ba2780f](https://github.com/gitpod-io/gitpod-sdk-go/commit/ba2780f5173f3e569527a24e9151e258e90df509)) + + +### Bug Fixes + +* **client:** return error on bad custom url instead of panic ([#64](https://github.com/gitpod-io/gitpod-sdk-go/issues/64)) ([656a0d1](https://github.com/gitpod-io/gitpod-sdk-go/commit/656a0d137cfa42afa6d540d80d306d254f4841aa)) +* **client:** unmarshal stream events into fresh memory ([#63](https://github.com/gitpod-io/gitpod-sdk-go/issues/63)) ([9cf0811](https://github.com/gitpod-io/gitpod-sdk-go/commit/9cf08112fe7b5e702311882b79f167956b09753f)) +* handle empty bodies in WithJSONSet ([6155e91](https://github.com/gitpod-io/gitpod-sdk-go/commit/6155e915016da00b885ed17f2452e0b48e1e4fd6)) +* **pagination:** handle errors when applying options ([8853449](https://github.com/gitpod-io/gitpod-sdk-go/commit/8853449c7377b7d2976647e4f84598513394b4ab)) +* **test:** return early after test failure ([#61](https://github.com/gitpod-io/gitpod-sdk-go/issues/61)) ([0295d21](https://github.com/gitpod-io/gitpod-sdk-go/commit/0295d214d2934e8d4789883255885d3e5ea474c9)) + + +### Chores + +* add request options to client tests ([#60](https://github.com/gitpod-io/gitpod-sdk-go/issues/60)) ([b575336](https://github.com/gitpod-io/gitpod-sdk-go/commit/b5753364f5bc6c5617205b2145ade577bc43ac72)) +* **ci:** add timeout thresholds for CI jobs ([41d7ceb](https://github.com/gitpod-io/gitpod-sdk-go/commit/41d7cebf060b9a6d6f432d2caa6a82032ca3408c)) +* **ci:** only use depot for staging repos ([9fbbeb1](https://github.com/gitpod-io/gitpod-sdk-go/commit/9fbbeb1fd88a3dc49943ef661c1d1c39da956fa2)) +* **docs:** document pre-request options ([f2e2e2e](https://github.com/gitpod-io/gitpod-sdk-go/commit/f2e2e2e6286c2641387a0fc11b7ada23d8afc9fc)) +* **docs:** improve security documentation ([#59](https://github.com/gitpod-io/gitpod-sdk-go/issues/59)) ([2d3d9e7](https://github.com/gitpod-io/gitpod-sdk-go/commit/2d3d9e7bdb95db8579085db1a23fa7f558b3ab2b)) +* fix typos ([#62](https://github.com/gitpod-io/gitpod-sdk-go/issues/62)) ([2ab745f](https://github.com/gitpod-io/gitpod-sdk-go/commit/2ab745f9136f7c6f4528f05e26c1c22a4b15b959)) +* **internal:** codegen related update ([a14dae0](https://github.com/gitpod-io/gitpod-sdk-go/commit/a14dae09ffdfec9ef7e1cb3c4b0c3ead27b14e14)) +* **internal:** codegen related update ([#56](https://github.com/gitpod-io/gitpod-sdk-go/issues/56)) ([dc521f6](https://github.com/gitpod-io/gitpod-sdk-go/commit/dc521f608d7475d2d98f64fe118433450ab5b2af)) +* **internal:** expand CI branch coverage ([7d00669](https://github.com/gitpod-io/gitpod-sdk-go/commit/7d00669f67ba5d7007feceb6f25863de34a237df)) +* **internal:** reduce CI branch coverage ([3b674ac](https://github.com/gitpod-io/gitpod-sdk-go/commit/3b674ac888efc38b3713547841770b6c77af2ac8)) +* **internal:** remove extra empty newlines ([#58](https://github.com/gitpod-io/gitpod-sdk-go/issues/58)) ([80ff63b](https://github.com/gitpod-io/gitpod-sdk-go/commit/80ff63b57f6df81750f4acaee91003902d2ddae6)) +* **tests:** improve enum examples ([#66](https://github.com/gitpod-io/gitpod-sdk-go/issues/66)) ([359070f](https://github.com/gitpod-io/gitpod-sdk-go/commit/359070f05052c648ae75a806ddd037c8eb28dbe7)) + + +### Documentation + +* update documentation links to be more uniform ([df86410](https://github.com/gitpod-io/gitpod-sdk-go/commit/df86410ba6bf256e7dc7ebd8b41fc5e15d762fa0)) +* update URLs from stainlessapi.com to stainless.com ([#53](https://github.com/gitpod-io/gitpod-sdk-go/issues/53)) ([a5f0af7](https://github.com/gitpod-io/gitpod-sdk-go/commit/a5f0af754b7fb4ef5d967aa53ce3ad22648826be)) + ## 0.4.0 (2025-02-21) Full Changelog: [v0.3.2...v0.4.0](https://github.com/gitpod-io/gitpod-sdk-go/compare/v0.3.2...v0.4.0) diff --git a/README.md b/README.md index 2f1cbb6..ef33925 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Go Reference -The Gitpod Go library provides convenient access to [the Gitpod REST -API](https://docs.gitpod.io) from applications written in Go. The full API of this library can be found in [api.md](api.md). +The Gitpod Go library provides convenient access to the [Gitpod REST API](https://docs.gitpod.io) +from applications written in Go. -It is generated with [Stainless](https://www.stainlessapi.com/). +It is generated with [Stainless](https://www.stainless.com/). ## Installation @@ -24,7 +24,7 @@ Or to pin the version: ```sh -go get -u 'github.com/gitpod-io/gitpod-sdk-go@v0.4.0' +go get -u 'github.com/gitpod-io/gitpod-sdk-go@v0.5.0' ``` diff --git a/SECURITY.md b/SECURITY.md index 0985c82..efd9088 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Reporting Security Issues -This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. -To report a security issue, please contact the Stainless team at security@stainlessapi.com. +To report a security issue, please contact the Stainless team at security@stainless.com. ## Responsible Disclosure @@ -16,11 +16,11 @@ before making any information public. ## Reporting Non-SDK Related Security Issues If you encounter security issues that are not directly related to SDKs but pertain to the services -or products provided by Gitpod please follow the respective company's security reporting guidelines. +or products provided by Gitpod, please follow the respective company's security reporting guidelines. ### Gitpod Terms and Policies -Please contact dev-feedback@gitpod.com for any questions or concerns regarding security of our services. +Please contact dev-feedback@gitpod.com for any questions or concerns regarding the security of our services. --- diff --git a/account.go b/account.go index 86bdf78..12ed09b 100644 --- a/account.go +++ b/account.go @@ -120,6 +120,30 @@ func (r *AccountService) GetSSOLoginURL(ctx context.Context, body AccountGetSSOL return } +// Lists organizations that the currently authenticated account can join. +// +// Use this method to: +// +// - Discover organizations associated with the account's email domain. +// - Allow users to join existing organizations. +// - Display potential organizations during onboarding. +// +// ### Examples +// +// - List joinable organizations: +// +// Retrieves a list of organizations the account can join. +// +// ```yaml +// {} +// ``` +func (r *AccountService) ListJoinableOrganizations(ctx context.Context, params AccountListJoinableOrganizationsParams, opts ...option.RequestOption) (res *AccountListJoinableOrganizationsResponse, err error) { + opts = append(r.Options[:], opts...) + path := "gitpod.v1.AccountService/ListJoinableOrganizations" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, params, &res, opts...) + return +} + // Lists available login providers with optional filtering. // // Use this method to: @@ -380,8 +404,11 @@ type Account struct { // Joda Time's // [`ISODateTimeFormat.dateTime()`]() // to obtain a formatter capable of generating timestamps in this format. - UpdatedAt time.Time `json:"updatedAt,required" format:"date-time"` - AvatarURL string `json:"avatarUrl"` + UpdatedAt time.Time `json:"updatedAt,required" format:"date-time"` + AvatarURL string `json:"avatarUrl"` + // joinables is deprecated. Use ListJoinableOrganizations instead. + // + // Deprecated: deprecated Joinables []JoinableOrganization `json:"joinables"` Memberships []AccountMembership `json:"memberships"` // organization_id is the ID of the organization the account is owned by if it's @@ -552,6 +579,27 @@ func (r accountGetSSOLoginURLResponseJSON) RawJSON() string { return r.raw } +type AccountListJoinableOrganizationsResponse struct { + JoinableOrganizations []JoinableOrganization `json:"joinableOrganizations"` + JSON accountListJoinableOrganizationsResponseJSON `json:"-"` +} + +// accountListJoinableOrganizationsResponseJSON contains the JSON metadata for the +// struct [AccountListJoinableOrganizationsResponse] +type accountListJoinableOrganizationsResponseJSON struct { + JoinableOrganizations apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *AccountListJoinableOrganizationsResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r accountListJoinableOrganizationsResponseJSON) RawJSON() string { + return r.raw +} + type AccountGetParams struct { Empty param.Field[bool] `json:"empty"` } @@ -579,6 +627,25 @@ func (r AccountGetSSOLoginURLParams) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } +type AccountListJoinableOrganizationsParams struct { + Token param.Field[string] `query:"token"` + PageSize param.Field[int64] `query:"pageSize"` + Empty param.Field[bool] `json:"empty"` +} + +func (r AccountListJoinableOrganizationsParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +// URLQuery serializes [AccountListJoinableOrganizationsParams]'s query parameters +// as `url.Values`. +func (r AccountListJoinableOrganizationsParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatComma, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + type AccountListLoginProvidersParams struct { Token param.Field[string] `query:"token"` PageSize param.Field[int64] `query:"pageSize"` diff --git a/account_test.go b/account_test.go index 67acb95..51b820f 100644 --- a/account_test.go +++ b/account_test.go @@ -89,6 +89,33 @@ func TestAccountGetSSOLoginURLWithOptionalParams(t *testing.T) { } } +func TestAccountListJoinableOrganizationsWithOptionalParams(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 := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + _, err := client.Accounts.ListJoinableOrganizations(context.TODO(), gitpod.AccountListJoinableOrganizationsParams{ + Token: gitpod.F("token"), + PageSize: gitpod.F(int64(0)), + Empty: gitpod.F(true), + }) + if err != nil { + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + func TestAccountListLoginProvidersWithOptionalParams(t *testing.T) { t.Skip("skipped: tests are disabled for the time being") baseURL := "http://localhost:4010" diff --git a/aliases.go b/aliases.go index f5cc265..8e7c024 100644 --- a/aliases.go +++ b/aliases.go @@ -55,6 +55,11 @@ type FieldValue = shared.FieldValue // This is an alias to an internal type. type FieldValueParam = shared.FieldValueParam +// Gateway represents a system gateway that provides access to services +// +// This is an alias to an internal type. +type Gateway = shared.Gateway + // This is an alias to an internal type. type OrganizationRole = shared.OrganizationRole @@ -88,6 +93,9 @@ const PrincipalEnvironment = shared.PrincipalEnvironment // This is an alias to an internal value. const PrincipalServiceAccount = shared.PrincipalServiceAccount +// This is an alias to an internal value. +const PrincipalRunnerManager = shared.PrincipalRunnerManager + // This is an alias to an internal type. type RunsOn = shared.RunsOn diff --git a/api.md b/api.md index 5aa7cbb..b5ca854 100644 --- a/api.md +++ b/api.md @@ -16,6 +16,7 @@ - shared.AutomationTrigger - shared.EnvironmentClass - shared.FieldValue +- shared.Gateway - shared.OrganizationRole - shared.Principal - shared.RunsOn @@ -41,12 +42,14 @@ Response Types: - gitpod.AccountGetResponse - gitpod.AccountDeleteResponse - gitpod.AccountGetSSOLoginURLResponse +- gitpod.AccountListJoinableOrganizationsResponse Methods: - client.Accounts.Get(ctx context.Context, body gitpod.AccountGetParams) (gitpod.AccountGetResponse, error) - client.Accounts.Delete(ctx context.Context, body gitpod.AccountDeleteParams) (gitpod.AccountDeleteResponse, error) - client.Accounts.GetSSOLoginURL(ctx context.Context, body gitpod.AccountGetSSOLoginURLParams) (gitpod.AccountGetSSOLoginURLResponse, error) +- client.Accounts.ListJoinableOrganizations(ctx context.Context, params gitpod.AccountListJoinableOrganizationsParams) (gitpod.AccountListJoinableOrganizationsResponse, error) - client.Accounts.ListLoginProviders(ctx context.Context, params gitpod.AccountListLoginProvidersParams) (pagination.LoginProvidersPage[gitpod.LoginProvider], error) # Editors @@ -85,11 +88,13 @@ Response Types: - gitpod.EnvironmentGetResponse - gitpod.EnvironmentUpdateResponse - gitpod.EnvironmentDeleteResponse +- gitpod.EnvironmentNewEnvironmentTokenResponse - gitpod.EnvironmentNewFromProjectResponse - gitpod.EnvironmentNewLogsTokenResponse - gitpod.EnvironmentMarkActiveResponse - gitpod.EnvironmentStartResponse - gitpod.EnvironmentStopResponse +- gitpod.EnvironmentUnarchiveResponse Methods: @@ -98,11 +103,13 @@ Methods: - client.Environments.Update(ctx context.Context, body gitpod.EnvironmentUpdateParams) (gitpod.EnvironmentUpdateResponse, error) - client.Environments.List(ctx context.Context, params gitpod.EnvironmentListParams) (pagination.EnvironmentsPage[gitpod.Environment], error) - client.Environments.Delete(ctx context.Context, body gitpod.EnvironmentDeleteParams) (gitpod.EnvironmentDeleteResponse, error) +- client.Environments.NewEnvironmentToken(ctx context.Context, body gitpod.EnvironmentNewEnvironmentTokenParams) (gitpod.EnvironmentNewEnvironmentTokenResponse, error) - client.Environments.NewFromProject(ctx context.Context, body gitpod.EnvironmentNewFromProjectParams) (gitpod.EnvironmentNewFromProjectResponse, error) - client.Environments.NewLogsToken(ctx context.Context, body gitpod.EnvironmentNewLogsTokenParams) (gitpod.EnvironmentNewLogsTokenResponse, error) - client.Environments.MarkActive(ctx context.Context, body gitpod.EnvironmentMarkActiveParams) (gitpod.EnvironmentMarkActiveResponse, error) - client.Environments.Start(ctx context.Context, body gitpod.EnvironmentStartParams) (gitpod.EnvironmentStartResponse, error) - client.Environments.Stop(ctx context.Context, body gitpod.EnvironmentStopParams) (gitpod.EnvironmentStopResponse, error) +- client.Environments.Unarchive(ctx context.Context, body gitpod.EnvironmentUnarchiveParams) (gitpod.EnvironmentUnarchiveResponse, error) ## Automations @@ -206,6 +213,12 @@ Methods: - client.Events.List(ctx context.Context, params gitpod.EventListParams) (pagination.EntriesPage[gitpod.EventListResponse], error) - client.Events.Watch(ctx context.Context, body gitpod.EventWatchParams) (gitpod.EventWatchResponse, error) +# Gateways + +Methods: + +- client.Gateways.List(ctx context.Context, params gitpod.GatewayListParams) (pagination.GatewaysPage[shared.Gateway], error) + # Groups Response Types: @@ -218,6 +231,10 @@ Methods: # Identity +Params Types: + +- gitpod.IDTokenVersion + Response Types: - gitpod.IdentityExchangeTokenResponse @@ -235,13 +252,13 @@ Methods: Params Types: - gitpod.InviteDomainsParam -- gitpod.Scope Response Types: - gitpod.InviteDomains - gitpod.Organization - gitpod.OrganizationMember +- gitpod.OrganizationTier - gitpod.OrganizationNewResponse - gitpod.OrganizationGetResponse - gitpod.OrganizationUpdateResponse @@ -255,7 +272,6 @@ Methods: - client.Organizations.New(ctx context.Context, body gitpod.OrganizationNewParams) (gitpod.OrganizationNewResponse, error) - client.Organizations.Get(ctx context.Context, body gitpod.OrganizationGetParams) (gitpod.OrganizationGetResponse, error) - client.Organizations.Update(ctx context.Context, body gitpod.OrganizationUpdateParams) (gitpod.OrganizationUpdateResponse, error) -- client.Organizations.List(ctx context.Context, params gitpod.OrganizationListParams) (pagination.OrganizationsPage[gitpod.Organization], error) - client.Organizations.Delete(ctx context.Context, body gitpod.OrganizationDeleteParams) (gitpod.OrganizationDeleteResponse, error) - client.Organizations.Join(ctx context.Context, body gitpod.OrganizationJoinParams) (gitpod.OrganizationJoinResponse, error) - client.Organizations.Leave(ctx context.Context, body gitpod.OrganizationLeaveParams) (gitpod.OrganizationLeaveResponse, error) @@ -296,6 +312,19 @@ Methods: - client.Organizations.Invites.Get(ctx context.Context, body gitpod.OrganizationInviteGetParams) (gitpod.OrganizationInviteGetResponse, error) - client.Organizations.Invites.GetSummary(ctx context.Context, body gitpod.OrganizationInviteGetSummaryParams) (gitpod.OrganizationInviteGetSummaryResponse, error) +## Policies + +Response Types: + +- gitpod.OrganizationPolicies +- gitpod.OrganizationPolicyGetResponse +- gitpod.OrganizationPolicyUpdateResponse + +Methods: + +- client.Organizations.Policies.Get(ctx context.Context, body gitpod.OrganizationPolicyGetParams) (gitpod.OrganizationPolicyGetResponse, error) +- client.Organizations.Policies.Update(ctx context.Context, body gitpod.OrganizationPolicyUpdateParams) (gitpod.OrganizationPolicyUpdateResponse, error) + ## SSOConfigurations Params Types: @@ -373,6 +402,8 @@ Methods: Params Types: +- gitpod.LogLevel +- gitpod.MetricsConfigurationParam - gitpod.RunnerConfigurationParam - gitpod.RunnerKind - gitpod.RunnerPhase @@ -382,6 +413,9 @@ Params Types: Response Types: +- gitpod.GatewayInfo +- gitpod.LogLevel +- gitpod.MetricsConfiguration - gitpod.Runner - gitpod.RunnerCapability - gitpod.RunnerConfiguration @@ -514,9 +548,14 @@ Methods: # Secrets +Params Types: + +- gitpod.SecretScopeParam + Response Types: - gitpod.Secret +- gitpod.SecretScope - gitpod.SecretNewResponse - gitpod.SecretDeleteResponse - gitpod.SecretGetValueResponse @@ -530,6 +569,16 @@ Methods: - client.Secrets.GetValue(ctx context.Context, body gitpod.SecretGetValueParams) (gitpod.SecretGetValueResponse, error) - client.Secrets.UpdateValue(ctx context.Context, body gitpod.SecretUpdateValueParams) (gitpod.SecretUpdateValueResponse, error) +# Usage + +Response Types: + +- gitpod.EnvironmentUsageRecord + +Methods: + +- client.Usage.ListEnvironmentRuntimeRecords(ctx context.Context, params gitpod.UsageListEnvironmentRuntimeRecordsParams) (pagination.RecordsPage[gitpod.EnvironmentUsageRecord], error) + # Users Response Types: @@ -543,6 +592,19 @@ Methods: - client.Users.GetAuthenticatedUser(ctx context.Context, body gitpod.UserGetAuthenticatedUserParams) (gitpod.UserGetAuthenticatedUserResponse, error) - client.Users.SetSuspended(ctx context.Context, body gitpod.UserSetSuspendedParams) (gitpod.UserSetSuspendedResponse, error) +## Dotfiles + +Response Types: + +- gitpod.DotfilesConfiguration +- gitpod.UserDotfileGetResponse +- gitpod.UserDotfileSetResponse + +Methods: + +- client.Users.Dotfiles.Get(ctx context.Context, body gitpod.UserDotfileGetParams) (gitpod.UserDotfileGetResponse, error) +- client.Users.Dotfiles.Set(ctx context.Context, body gitpod.UserDotfileSetParams) (gitpod.UserDotfileSetResponse, error) + ## Pats Response Types: diff --git a/client.go b/client.go index 9b1d2da..fb9ea61 100644 --- a/client.go +++ b/client.go @@ -20,25 +20,36 @@ type Client struct { Editors *EditorService Environments *EnvironmentService Events *EventService + Gateways *GatewayService Groups *GroupService Identity *IdentityService Organizations *OrganizationService Projects *ProjectService Runners *RunnerService Secrets *SecretService + Usage *UsageService Users *UserService } -// NewClient generates a new client with the default option read from the -// environment (GITPOD_API_KEY). The option passed in as arguments are applied -// after these default arguments, and all option will be passed down to the -// services and requests that this client makes. -func NewClient(opts ...option.RequestOption) (r *Client) { +// DefaultClientOptions read from the environment (GITPOD_API_KEY, +// GITPOD_BASE_URL). This should be used to initialize new clients. +func DefaultClientOptions() []option.RequestOption { defaults := []option.RequestOption{option.WithEnvironmentProduction()} + if o, ok := os.LookupEnv("GITPOD_BASE_URL"); ok { + defaults = append(defaults, option.WithBaseURL(o)) + } if o, ok := os.LookupEnv("GITPOD_API_KEY"); ok { defaults = append(defaults, option.WithBearerToken(o)) } - opts = append(defaults, opts...) + return defaults +} + +// NewClient generates a new client with the default option read from the +// environment (GITPOD_API_KEY, GITPOD_BASE_URL). The option passed in as arguments +// are applied after these default arguments, and all option will be passed down to +// the services and requests that this client makes. +func NewClient(opts ...option.RequestOption) (r *Client) { + opts = append(DefaultClientOptions(), opts...) r = &Client{Options: opts} @@ -46,12 +57,14 @@ func NewClient(opts ...option.RequestOption) (r *Client) { r.Editors = NewEditorService(opts...) r.Environments = NewEnvironmentService(opts...) r.Events = NewEventService(opts...) + r.Gateways = NewGatewayService(opts...) r.Groups = NewGroupService(opts...) r.Identity = NewIdentityService(opts...) r.Organizations = NewOrganizationService(opts...) r.Projects = NewProjectService(opts...) r.Runners = NewRunnerService(opts...) r.Secrets = NewSecretService(opts...) + r.Usage = NewUsageService(opts...) r.Users = NewUserService(opts...) return diff --git a/client_test.go b/client_test.go index 4375020..a6213e2 100644 --- a/client_test.go +++ b/client_test.go @@ -26,6 +26,7 @@ func (t *closureTransport) RoundTrip(req *http.Request) (*http.Response, error) func TestUserAgentHeader(t *testing.T) { var userAgent string client := gitpod.NewClient( + option.WithBearerToken("My Bearer Token"), option.WithHTTPClient(&http.Client{ Transport: &closureTransport{ fn: func(req *http.Request) (*http.Response, error) { @@ -46,6 +47,7 @@ func TestUserAgentHeader(t *testing.T) { func TestRetryAfter(t *testing.T) { retryCountHeaders := make([]string, 0) client := gitpod.NewClient( + option.WithBearerToken("My Bearer Token"), option.WithHTTPClient(&http.Client{ Transport: &closureTransport{ fn: func(req *http.Request) (*http.Response, error) { @@ -79,6 +81,7 @@ func TestRetryAfter(t *testing.T) { func TestDeleteRetryCountHeader(t *testing.T) { retryCountHeaders := make([]string, 0) client := gitpod.NewClient( + option.WithBearerToken("My Bearer Token"), option.WithHTTPClient(&http.Client{ Transport: &closureTransport{ fn: func(req *http.Request) (*http.Response, error) { @@ -108,6 +111,7 @@ func TestDeleteRetryCountHeader(t *testing.T) { func TestOverwriteRetryCountHeader(t *testing.T) { retryCountHeaders := make([]string, 0) client := gitpod.NewClient( + option.WithBearerToken("My Bearer Token"), option.WithHTTPClient(&http.Client{ Transport: &closureTransport{ fn: func(req *http.Request) (*http.Response, error) { @@ -137,6 +141,7 @@ func TestOverwriteRetryCountHeader(t *testing.T) { func TestRetryAfterMs(t *testing.T) { attempts := 0 client := gitpod.NewClient( + option.WithBearerToken("My Bearer Token"), option.WithHTTPClient(&http.Client{ Transport: &closureTransport{ fn: func(req *http.Request) (*http.Response, error) { @@ -162,6 +167,7 @@ func TestRetryAfterMs(t *testing.T) { func TestContextCancel(t *testing.T) { client := gitpod.NewClient( + option.WithBearerToken("My Bearer Token"), option.WithHTTPClient(&http.Client{ Transport: &closureTransport{ fn: func(req *http.Request) (*http.Response, error) { @@ -181,6 +187,7 @@ func TestContextCancel(t *testing.T) { func TestContextCancelDelay(t *testing.T) { client := gitpod.NewClient( + option.WithBearerToken("My Bearer Token"), option.WithHTTPClient(&http.Client{ Transport: &closureTransport{ fn: func(req *http.Request) (*http.Response, error) { @@ -208,6 +215,7 @@ func TestContextDeadline(t *testing.T) { go func() { client := gitpod.NewClient( + option.WithBearerToken("My Bearer Token"), option.WithHTTPClient(&http.Client{ Transport: &closureTransport{ fn: func(req *http.Request) (*http.Response, error) { diff --git a/editor.go b/editor.go index 0b634d5..f37c3ff 100644 --- a/editor.go +++ b/editor.go @@ -57,7 +57,8 @@ func (r *EditorService) Get(ctx context.Context, body EditorGetParams, opts ...o return } -// Lists all available code editors. +// Lists all available code editors, optionally filtered to those allowed in an +// organization. // // Use this method to: // @@ -76,6 +77,18 @@ func (r *EditorService) Get(ctx context.Context, body EditorGetParams, opts ...o // pagination: // pageSize: 20 // ``` +// +// - List editors available to the organization: +// +// Shows all available editors that are allowed by the policies enforced in the +// organization with pagination. +// +// ```yaml +// pagination: +// pageSize: 20 +// filter: +// allowedByPolicy: true +// ``` func (r *EditorService) List(ctx context.Context, params EditorListParams, opts ...option.RequestOption) (res *pagination.EditorsPage[Editor], err error) { var raw *http.Response opts = append(r.Options[:], opts...) @@ -93,7 +106,8 @@ func (r *EditorService) List(ctx context.Context, params EditorListParams, opts return res, nil } -// Lists all available code editors. +// Lists all available code editors, optionally filtered to those allowed in an +// organization. // // Use this method to: // @@ -112,6 +126,18 @@ func (r *EditorService) List(ctx context.Context, params EditorListParams, opts // pagination: // pageSize: 20 // ``` +// +// - List editors available to the organization: +// +// Shows all available editors that are allowed by the policies enforced in the +// organization with pagination. +// +// ```yaml +// pagination: +// pageSize: 20 +// filter: +// allowedByPolicy: true +// ``` func (r *EditorService) ListAutoPaging(ctx context.Context, params EditorListParams, opts ...option.RequestOption) *pagination.EditorsPageAutoPager[Editor] { return pagination.NewEditorsPageAutoPager(r.List(ctx, params, opts...)) } @@ -231,6 +257,8 @@ func (r EditorGetParams) MarshalJSON() (data []byte, err error) { type EditorListParams struct { Token param.Field[string] `query:"token"` PageSize param.Field[int64] `query:"pageSize"` + // filter contains the filter options for listing editors + Filter param.Field[EditorListParamsFilter] `json:"filter"` // pagination contains the pagination options for listing environments Pagination param.Field[EditorListParamsPagination] `json:"pagination"` } @@ -247,6 +275,17 @@ func (r EditorListParams) URLQuery() (v url.Values) { }) } +// filter contains the filter options for listing editors +type EditorListParamsFilter struct { + // allowed_by_policy filters the response to only editors that are allowed by the + // policies enforced in the organization + AllowedByPolicy param.Field[bool] `json:"allowedByPolicy"` +} + +func (r EditorListParamsFilter) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + // pagination contains the pagination options for listing environments type EditorListParamsPagination struct { // Token for the next set of results that was returned as next_token of a diff --git a/editor_test.go b/editor_test.go index 88c1933..fd6f930 100644 --- a/editor_test.go +++ b/editor_test.go @@ -54,6 +54,9 @@ func TestEditorListWithOptionalParams(t *testing.T) { _, err := client.Editors.List(context.TODO(), gitpod.EditorListParams{ Token: gitpod.F("token"), PageSize: gitpod.F(int64(0)), + Filter: gitpod.F(gitpod.EditorListParamsFilter{ + AllowedByPolicy: gitpod.F(true), + }), Pagination: gitpod.F(gitpod.EditorListParamsPagination{ Token: gitpod.F("token"), PageSize: gitpod.F(int64(20)), diff --git a/environment.go b/environment.go index c5a19ee..180ec8a 100644 --- a/environment.go +++ b/environment.go @@ -318,6 +318,27 @@ func (r *EnvironmentService) Delete(ctx context.Context, body EnvironmentDeleteP return } +// Creates an access token for the environment. +// +// Generated tokens are valid for one hour and provide environment-specific access +// permissions. The token is scoped to a specific environment. +// +// ### Examples +// +// - Generate environment token: +// +// Creates a temporary access token for accessing an environment. +// +// ```yaml +// environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048" +// ``` +func (r *EnvironmentService) NewEnvironmentToken(ctx context.Context, body EnvironmentNewEnvironmentTokenParams, opts ...option.RequestOption) (res *EnvironmentNewEnvironmentTokenResponse, err error) { + opts = append(r.Options[:], opts...) + path := "gitpod.v1.EnvironmentService/CreateEnvironmentAccessToken" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + // Creates an environment from an existing project configuration and starts it. // // This method uses project settings as defaults but allows overriding specific @@ -443,6 +464,22 @@ func (r *EnvironmentService) Stop(ctx context.Context, body EnvironmentStopParam return } +// Unarchives an environment. +// +// ### Examples +// +// - Unarchive an environment: +// +// ```yaml +// environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048" +// ``` +func (r *EnvironmentService) Unarchive(ctx context.Context, body EnvironmentUnarchiveParams, opts ...option.RequestOption) (res *EnvironmentUnarchiveResponse, err error) { + opts = append(r.Options[:], opts...) + path := "gitpod.v1.EnvironmentService/UnarchiveEnvironment" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + // Admission level describes who can access an environment instance and its ports. type AdmissionLevel string @@ -544,6 +581,9 @@ type EnvironmentMetadata struct { // annotations are key/value pairs that gets attached to the environment. // +internal - not yet implemented Annotations map[string]string `json:"annotations"` + // Time when the Environment was archived. If not set, the environment is not + // archived. + ArchivedAt time.Time `json:"archivedAt" format:"date-time"` // Time when the Environment was created. CreatedAt time.Time `json:"createdAt" format:"date-time"` // creator is the identity of the creator of the environment @@ -570,6 +610,7 @@ type EnvironmentMetadata struct { // [EnvironmentMetadata] type environmentMetadataJSON struct { Annotations apijson.Field + ArchivedAt apijson.Field CreatedAt apijson.Field Creator apijson.Field LastStartedAt apijson.Field @@ -731,6 +772,9 @@ func (r environmentSpecContentJSON) RawJSON() string { // devcontainer is the devcontainer spec of the environment type EnvironmentSpecDevcontainer struct { + // default_devcontainer_image is the default image that is used to start the + // devcontainer if no devcontainer config file is found + DefaultDevcontainerImage string `json:"defaultDevcontainerImage"` // devcontainer_file_path is the path to the devcontainer file relative to the repo // root path must not be absolute (start with a /): // @@ -747,11 +791,12 @@ type EnvironmentSpecDevcontainer struct { // environmentSpecDevcontainerJSON contains the JSON metadata for the struct // [EnvironmentSpecDevcontainer] type environmentSpecDevcontainerJSON struct { - DevcontainerFilePath apijson.Field - Dotfiles apijson.Field - Session apijson.Field - raw string - ExtraFields map[string]apijson.Field + DefaultDevcontainerImage apijson.Field + DevcontainerFilePath apijson.Field + Dotfiles apijson.Field + Session apijson.Field + raw string + ExtraFields map[string]apijson.Field } func (r *EnvironmentSpecDevcontainer) UnmarshalJSON(data []byte) (err error) { @@ -839,6 +884,8 @@ func (r environmentSpecPortJSON) RawJSON() string { } type EnvironmentSpecSecret struct { + // id is the unique identifier of the secret. + ID string `json:"id"` // container_registry_basic_auth_host is the hostname of the container registry // that supports basic auth ContainerRegistryBasicAuthHost string `json:"containerRegistryBasicAuthHost"` @@ -861,6 +908,7 @@ type EnvironmentSpecSecret struct { // environmentSpecSecretJSON contains the JSON metadata for the struct // [EnvironmentSpecSecret] type environmentSpecSecretJSON struct { + ID apijson.Field ContainerRegistryBasicAuthHost apijson.Field EnvironmentVariable apijson.Field FilePath apijson.Field @@ -997,6 +1045,9 @@ func (r EnvironmentSpecContentParam) MarshalJSON() (data []byte, err error) { // devcontainer is the devcontainer spec of the environment type EnvironmentSpecDevcontainerParam struct { + // default_devcontainer_image is the default image that is used to start the + // devcontainer if no devcontainer config file is found + DefaultDevcontainerImage param.Field[string] `json:"defaultDevcontainerImage"` // devcontainer_file_path is the path to the devcontainer file relative to the repo // root path must not be absolute (start with a /): // @@ -1048,6 +1099,8 @@ func (r EnvironmentSpecPortParam) MarshalJSON() (data []byte, err error) { } type EnvironmentSpecSecretParam struct { + // id is the unique identifier of the secret. + ID param.Field[string] `json:"id"` // container_registry_basic_auth_host is the hostname of the container registry // that supports basic auth ContainerRegistryBasicAuthHost param.Field[string] `json:"containerRegistryBasicAuthHost"` @@ -1176,8 +1229,11 @@ type EnvironmentStatusAutomationsFile struct { Phase EnvironmentStatusAutomationsFilePhase `json:"phase"` // session is the automations file session that is currently applied in the // environment. - Session string `json:"session"` - JSON environmentStatusAutomationsFileJSON `json:"-"` + Session string `json:"session"` + // warning_message contains warnings, e.g. when no triggers are defined in the + // automations file. + WarningMessage string `json:"warningMessage"` + JSON environmentStatusAutomationsFileJSON `json:"-"` } // environmentStatusAutomationsFileJSON contains the JSON metadata for the struct @@ -1188,6 +1244,7 @@ type environmentStatusAutomationsFileJSON struct { FailureMessage apijson.Field Phase apijson.Field Session apijson.Field + WarningMessage apijson.Field raw string ExtraFields map[string]apijson.Field } @@ -1700,6 +1757,8 @@ func (r EnvironmentStatusRunnerAckStatusCode) IsKnown() bool { } type EnvironmentStatusSecret struct { + // id is the unique identifier of the secret. + ID string `json:"id"` // failure_message contains the reason the secret failed to be materialize. FailureMessage string `json:"failureMessage"` Phase EnvironmentStatusSecretsPhase `json:"phase"` @@ -1715,6 +1774,7 @@ type EnvironmentStatusSecret struct { // environmentStatusSecretJSON contains the JSON metadata for the struct // [EnvironmentStatusSecret] type environmentStatusSecretJSON struct { + ID apijson.Field FailureMessage apijson.Field Phase apijson.Field SecretName apijson.Field @@ -1844,6 +1904,28 @@ type EnvironmentUpdateResponse = interface{} type EnvironmentDeleteResponse = interface{} +type EnvironmentNewEnvironmentTokenResponse struct { + // access_token is the token that can be used for environment authentication + AccessToken string `json:"accessToken,required"` + JSON environmentNewEnvironmentTokenResponseJSON `json:"-"` +} + +// environmentNewEnvironmentTokenResponseJSON contains the JSON metadata for the +// struct [EnvironmentNewEnvironmentTokenResponse] +type environmentNewEnvironmentTokenResponseJSON struct { + AccessToken apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EnvironmentNewEnvironmentTokenResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r environmentNewEnvironmentTokenResponseJSON) RawJSON() string { + return r.raw +} + type EnvironmentNewFromProjectResponse struct { // +resource get environment Environment Environment `json:"environment,required"` @@ -1894,6 +1976,8 @@ type EnvironmentStartResponse = interface{} type EnvironmentStopResponse = interface{} +type EnvironmentUnarchiveResponse = interface{} + type EnvironmentNewParams struct { // spec is the configuration of the environment that's required for the to start // the environment @@ -1917,15 +2001,24 @@ type EnvironmentUpdateParams struct { // environment_id specifies which environment should be updated. // // +required - EnvironmentID param.Field[string] `json:"environmentId" format:"uuid"` - Metadata param.Field[interface{}] `json:"metadata"` - Spec param.Field[EnvironmentUpdateParamsSpec] `json:"spec"` + EnvironmentID param.Field[string] `json:"environmentId" format:"uuid"` + Metadata param.Field[EnvironmentUpdateParamsMetadata] `json:"metadata"` + Spec param.Field[EnvironmentUpdateParamsSpec] `json:"spec"` } func (r EnvironmentUpdateParams) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } +type EnvironmentUpdateParamsMetadata struct { + // name is the user-defined display name of the environment + Name param.Field[string] `json:"name"` +} + +func (r EnvironmentUpdateParamsMetadata) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + type EnvironmentUpdateParamsSpec struct { // automations_file is the automations file spec of the environment AutomationsFile param.Field[EnvironmentUpdateParamsSpecAutomationsFile] `json:"automationsFile"` @@ -2049,6 +2142,8 @@ func (r EnvironmentListParams) URLQuery() (v url.Values) { } type EnvironmentListParamsFilter struct { + // archival_status filters the response based on environment archive status + ArchivalStatus param.Field[EnvironmentListParamsFilterArchivalStatus] `json:"archivalStatus"` // creator_ids filters the response to only Environments created by specified // members CreatorIDs param.Field[[]string] `json:"creatorIds" format:"uuid"` @@ -2069,6 +2164,24 @@ func (r EnvironmentListParamsFilter) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } +// archival_status filters the response based on environment archive status +type EnvironmentListParamsFilterArchivalStatus string + +const ( + EnvironmentListParamsFilterArchivalStatusArchivalStatusUnspecified EnvironmentListParamsFilterArchivalStatus = "ARCHIVAL_STATUS_UNSPECIFIED" + EnvironmentListParamsFilterArchivalStatusArchivalStatusActive EnvironmentListParamsFilterArchivalStatus = "ARCHIVAL_STATUS_ACTIVE" + EnvironmentListParamsFilterArchivalStatusArchivalStatusArchived EnvironmentListParamsFilterArchivalStatus = "ARCHIVAL_STATUS_ARCHIVED" + EnvironmentListParamsFilterArchivalStatusArchivalStatusAll EnvironmentListParamsFilterArchivalStatus = "ARCHIVAL_STATUS_ALL" +) + +func (r EnvironmentListParamsFilterArchivalStatus) IsKnown() bool { + switch r { + case EnvironmentListParamsFilterArchivalStatusArchivalStatusUnspecified, EnvironmentListParamsFilterArchivalStatusArchivalStatusActive, EnvironmentListParamsFilterArchivalStatusArchivalStatusArchived, EnvironmentListParamsFilterArchivalStatusArchivalStatusAll: + return true + } + return false +} + // pagination contains the pagination options for listing environments type EnvironmentListParamsPagination struct { // Token for the next set of results that was returned as next_token of a @@ -2099,6 +2212,16 @@ func (r EnvironmentDeleteParams) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } +type EnvironmentNewEnvironmentTokenParams struct { + // environment_id specifies the environment for which the access token should be + // created. + EnvironmentID param.Field[string] `json:"environmentId,required" format:"uuid"` +} + +func (r EnvironmentNewEnvironmentTokenParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + type EnvironmentNewFromProjectParams struct { ProjectID param.Field[string] `json:"projectId" format:"uuid"` // Spec is the configuration of the environment that's required for the runner to @@ -2153,3 +2276,14 @@ type EnvironmentStopParams struct { func (r EnvironmentStopParams) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } + +type EnvironmentUnarchiveParams struct { + // environment_id specifies the environment to unarchive. + // + // +required + EnvironmentID param.Field[string] `json:"environmentId" format:"uuid"` +} + +func (r EnvironmentUnarchiveParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} diff --git a/environment_test.go b/environment_test.go index 528434d..3f54cdf 100644 --- a/environment_test.go +++ b/environment_test.go @@ -55,7 +55,8 @@ func TestEnvironmentNewWithOptionalParams(t *testing.T) { }), DesiredPhase: gitpod.F(gitpod.EnvironmentPhaseUnspecified), Devcontainer: gitpod.F(gitpod.EnvironmentSpecDevcontainerParam{ - DevcontainerFilePath: gitpod.F("devcontainerFilePath"), + DefaultDevcontainerImage: gitpod.F("defaultDevcontainerImage"), + DevcontainerFilePath: gitpod.F("devcontainerFilePath"), Dotfiles: gitpod.F(gitpod.EnvironmentSpecDevcontainerDotfilesParam{ Repository: gitpod.F("https://example.com"), }), @@ -71,6 +72,7 @@ func TestEnvironmentNewWithOptionalParams(t *testing.T) { Port: gitpod.F(int64(1)), }}), Secrets: gitpod.F([]gitpod.EnvironmentSpecSecretParam{{ + ID: gitpod.F("id"), ContainerRegistryBasicAuthHost: gitpod.F("containerRegistryBasicAuthHost"), EnvironmentVariable: gitpod.F("environmentVariable"), FilePath: gitpod.F("filePath"), @@ -139,7 +141,9 @@ func TestEnvironmentUpdateWithOptionalParams(t *testing.T) { ) _, err := client.Environments.Update(context.TODO(), gitpod.EnvironmentUpdateParams{ EnvironmentID: gitpod.F("07e03a28-65a5-4d98-b532-8ea67b188048"), - Metadata: gitpod.F[any](map[string]interface{}{}), + Metadata: gitpod.F(gitpod.EnvironmentUpdateParamsMetadata{ + Name: gitpod.F("name"), + }), Spec: gitpod.F(gitpod.EnvironmentUpdateParamsSpec{ AutomationsFile: gitpod.F(gitpod.EnvironmentUpdateParamsSpecAutomationsFile{ AutomationsFilePath: gitpod.F("automationsFilePath"), @@ -208,11 +212,12 @@ func TestEnvironmentListWithOptionalParams(t *testing.T) { Token: gitpod.F("token"), PageSize: gitpod.F(int64(0)), Filter: gitpod.F(gitpod.EnvironmentListParamsFilter{ - CreatorIDs: gitpod.F([]string{"f53d2330-3795-4c5d-a1f3-453121af9c60"}), - ProjectIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}), - RunnerIDs: gitpod.F([]string{"e6aa9c54-89d3-42c1-ac31-bd8d8f1concentrate"}), - RunnerKinds: gitpod.F([]gitpod.RunnerKind{gitpod.RunnerKindUnspecified}), - StatusPhases: gitpod.F([]gitpod.EnvironmentPhase{gitpod.EnvironmentPhaseUnspecified}), + ArchivalStatus: gitpod.F(gitpod.EnvironmentListParamsFilterArchivalStatusArchivalStatusUnspecified), + CreatorIDs: gitpod.F([]string{"f53d2330-3795-4c5d-a1f3-453121af9c60"}), + ProjectIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}), + RunnerIDs: gitpod.F([]string{"e6aa9c54-89d3-42c1-ac31-bd8d8f1concentrate"}), + RunnerKinds: gitpod.F([]gitpod.RunnerKind{gitpod.RunnerKindUnspecified}), + StatusPhases: gitpod.F([]gitpod.EnvironmentPhase{gitpod.EnvironmentPhaseUnspecified}), }), Pagination: gitpod.F(gitpod.EnvironmentListParamsPagination{ Token: gitpod.F("token"), @@ -254,6 +259,31 @@ func TestEnvironmentDeleteWithOptionalParams(t *testing.T) { } } +func TestEnvironmentNewEnvironmentToken(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 := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + _, err := client.Environments.NewEnvironmentToken(context.TODO(), gitpod.EnvironmentNewEnvironmentTokenParams{ + EnvironmentID: gitpod.F("07e03a28-65a5-4d98-b532-8ea67b188048"), + }) + if err != nil { + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + func TestEnvironmentNewFromProjectWithOptionalParams(t *testing.T) { t.Skip("skipped: tests are disabled for the time being") baseURL := "http://localhost:4010" @@ -296,7 +326,8 @@ func TestEnvironmentNewFromProjectWithOptionalParams(t *testing.T) { }), DesiredPhase: gitpod.F(gitpod.EnvironmentPhaseUnspecified), Devcontainer: gitpod.F(gitpod.EnvironmentSpecDevcontainerParam{ - DevcontainerFilePath: gitpod.F("devcontainerFilePath"), + DefaultDevcontainerImage: gitpod.F("defaultDevcontainerImage"), + DevcontainerFilePath: gitpod.F("devcontainerFilePath"), Dotfiles: gitpod.F(gitpod.EnvironmentSpecDevcontainerDotfilesParam{ Repository: gitpod.F("https://example.com"), }), @@ -312,6 +343,7 @@ func TestEnvironmentNewFromProjectWithOptionalParams(t *testing.T) { Port: gitpod.F(int64(1)), }}), Secrets: gitpod.F([]gitpod.EnvironmentSpecSecretParam{{ + ID: gitpod.F("id"), ContainerRegistryBasicAuthHost: gitpod.F("containerRegistryBasicAuthHost"), EnvironmentVariable: gitpod.F("environmentVariable"), FilePath: gitpod.F("filePath"), @@ -443,3 +475,28 @@ func TestEnvironmentStopWithOptionalParams(t *testing.T) { t.Fatalf("err should be nil: %s", err.Error()) } } + +func TestEnvironmentUnarchiveWithOptionalParams(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 := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + _, err := client.Environments.Unarchive(context.TODO(), gitpod.EnvironmentUnarchiveParams{ + EnvironmentID: gitpod.F("07e03a28-65a5-4d98-b532-8ea67b188048"), + }) + if err != nil { + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/environmentautomation_test.go b/environmentautomation_test.go index 948ceea..1eba091 100644 --- a/environmentautomation_test.go +++ b/environmentautomation_test.go @@ -44,7 +44,7 @@ func TestEnvironmentAutomationUpsertWithOptionalParams(t *testing.T) { Image: gitpod.F("x"), }), }), - TriggeredBy: gitpod.F([]gitpod.AutomationsFileServicesTriggeredBy{gitpod.AutomationsFileServicesTriggeredByManual}), + TriggeredBy: gitpod.F([]gitpod.AutomationsFileServicesTriggeredBy{gitpod.AutomationsFileServicesTriggeredByPostDevcontainerStart}), }, }), Tasks: gitpod.F(map[string]gitpod.AutomationsFileTaskParam{ @@ -59,7 +59,7 @@ func TestEnvironmentAutomationUpsertWithOptionalParams(t *testing.T) { Image: gitpod.F("x"), }), }), - TriggeredBy: gitpod.F([]gitpod.AutomationsFileTasksTriggeredBy{gitpod.AutomationsFileTasksTriggeredByManual}), + TriggeredBy: gitpod.F([]gitpod.AutomationsFileTasksTriggeredBy{gitpod.AutomationsFileTasksTriggeredByPostEnvironmentStart}), }, }), }), diff --git a/environmentautomationservice.go b/environmentautomationservice.go index e5873d1..0a62d99 100644 --- a/environmentautomationservice.go +++ b/environmentautomationservice.go @@ -577,6 +577,9 @@ type ServiceStatus struct { FailureMessage string `json:"failureMessage"` // log_url contains the URL at which the service logs can be accessed. LogURL string `json:"logUrl"` + // output contains the output of the service. setting an output field to empty + // string will unset it. + Output map[string]string `json:"output"` // phase is the current phase of the service. Phase ServicePhase `json:"phase"` // session is the current session of the service. @@ -594,6 +597,7 @@ type ServiceStatus struct { type serviceStatusJSON struct { FailureMessage apijson.Field LogURL apijson.Field + Output apijson.Field Phase apijson.Field Session apijson.Field StatusVersion apijson.Field @@ -738,10 +742,12 @@ func (r EnvironmentAutomationServiceUpdateParamsSpecCommands) MarshalJSON() (dat // client of this API you are not expected to provide this field. Updating this // field requires the `environmentservice:update_status` permission. type EnvironmentAutomationServiceUpdateParamsStatus struct { - FailureMessage param.Field[string] `json:"failureMessage"` - LogURL param.Field[string] `json:"logUrl"` - Phase param.Field[ServicePhase] `json:"phase"` - Session param.Field[string] `json:"session"` + FailureMessage param.Field[string] `json:"failureMessage"` + LogURL param.Field[string] `json:"logUrl"` + // setting an output field to empty string will unset it. + Output param.Field[map[string]string] `json:"output"` + Phase param.Field[ServicePhase] `json:"phase"` + Session param.Field[string] `json:"session"` } func (r EnvironmentAutomationServiceUpdateParamsStatus) MarshalJSON() (data []byte, err error) { diff --git a/environmentautomationservice_test.go b/environmentautomationservice_test.go index 6699738..df89f59 100644 --- a/environmentautomationservice_test.go +++ b/environmentautomationservice_test.go @@ -138,8 +138,11 @@ func TestEnvironmentAutomationServiceUpdateWithOptionalParams(t *testing.T) { Status: gitpod.F(gitpod.EnvironmentAutomationServiceUpdateParamsStatus{ FailureMessage: gitpod.F("failureMessage"), LogURL: gitpod.F("logUrl"), - Phase: gitpod.F(gitpod.ServicePhaseUnspecified), - Session: gitpod.F("session"), + Output: gitpod.F(map[string]string{ + "foo": "string", + }), + Phase: gitpod.F(gitpod.ServicePhaseUnspecified), + Session: gitpod.F("session"), }), }) if err != nil { diff --git a/environmentautomationtaskexecution_test.go b/environmentautomationtaskexecution_test.go index 849eed1..fb63e66 100644 --- a/environmentautomationtaskexecution_test.go +++ b/environmentautomationtaskexecution_test.go @@ -57,7 +57,7 @@ func TestEnvironmentAutomationTaskExecutionListWithOptionalParams(t *testing.T) PageSize: gitpod.F(int64(0)), Filter: gitpod.F(gitpod.EnvironmentAutomationTaskExecutionListParamsFilter{ EnvironmentIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}), - Phases: gitpod.F([]shared.TaskExecutionPhase{shared.TaskExecutionPhaseUnspecified, shared.TaskExecutionPhasePending}), + Phases: gitpod.F([]shared.TaskExecutionPhase{shared.TaskExecutionPhaseRunning, shared.TaskExecutionPhaseFailed}), TaskIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}), TaskReferences: gitpod.F([]string{"string"}), }), diff --git a/event.go b/event.go index 7987a2a..2e83dac 100644 --- a/event.go +++ b/event.go @@ -179,11 +179,17 @@ const ( ResourceTypeSecret ResourceType = "RESOURCE_TYPE_SECRET" ResourceTypeSSOConfig ResourceType = "RESOURCE_TYPE_SSO_CONFIG" ResourceTypeDomainVerification ResourceType = "RESOURCE_TYPE_DOMAIN_VERIFICATION" + ResourceTypeAgentExecution ResourceType = "RESOURCE_TYPE_AGENT_EXECUTION" + ResourceTypeRunnerLlmIntegration ResourceType = "RESOURCE_TYPE_RUNNER_LLM_INTEGRATION" + ResourceTypeAgent ResourceType = "RESOURCE_TYPE_AGENT" + ResourceTypeEnvironmentSession ResourceType = "RESOURCE_TYPE_ENVIRONMENT_SESSION" + ResourceTypeUserSecret ResourceType = "RESOURCE_TYPE_USER_SECRET" + ResourceTypeOrganizationPolicy ResourceType = "RESOURCE_TYPE_ORGANIZATION_POLICY" ) func (r ResourceType) IsKnown() bool { switch r { - case ResourceTypeUnspecified, ResourceTypeEnvironment, ResourceTypeRunner, ResourceTypeProject, ResourceTypeTask, ResourceTypeTaskExecution, ResourceTypeService, ResourceTypeOrganization, ResourceTypeUser, ResourceTypeEnvironmentClass, ResourceTypeRunnerScmIntegration, ResourceTypeHostAuthenticationToken, ResourceTypeGroup, ResourceTypePersonalAccessToken, ResourceTypeUserPreference, ResourceTypeServiceAccount, ResourceTypeSecret, ResourceTypeSSOConfig, ResourceTypeDomainVerification: + case ResourceTypeUnspecified, ResourceTypeEnvironment, ResourceTypeRunner, ResourceTypeProject, ResourceTypeTask, ResourceTypeTaskExecution, ResourceTypeService, ResourceTypeOrganization, ResourceTypeUser, ResourceTypeEnvironmentClass, ResourceTypeRunnerScmIntegration, ResourceTypeHostAuthenticationToken, ResourceTypeGroup, ResourceTypePersonalAccessToken, ResourceTypeUserPreference, ResourceTypeServiceAccount, ResourceTypeSecret, ResourceTypeSSOConfig, ResourceTypeDomainVerification, ResourceTypeAgentExecution, ResourceTypeRunnerLlmIntegration, ResourceTypeAgent, ResourceTypeEnvironmentSession, ResourceTypeUserSecret, ResourceTypeOrganizationPolicy: return true } return false diff --git a/event_test.go b/event_test.go index 7157557..88747dc 100644 --- a/event_test.go +++ b/event_test.go @@ -32,7 +32,7 @@ func TestEventListWithOptionalParams(t *testing.T) { PageSize: gitpod.F(int64(0)), Filter: gitpod.F(gitpod.EventListParamsFilter{ ActorIDs: gitpod.F([]string{"d2c94c27-3b76-4a42-b88c-95a85e392c68"}), - ActorPrincipals: gitpod.F([]shared.Principal{shared.PrincipalUnspecified}), + ActorPrincipals: gitpod.F([]shared.Principal{shared.PrincipalUser}), SubjectIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}), SubjectTypes: gitpod.F([]gitpod.ResourceType{gitpod.ResourceTypeUnspecified}), }), diff --git a/gateway.go b/gateway.go new file mode 100644 index 0000000..ffe14da --- /dev/null +++ b/gateway.go @@ -0,0 +1,92 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package gitpod + +import ( + "context" + "net/http" + "net/url" + + "github.com/gitpod-io/gitpod-sdk-go/internal/apijson" + "github.com/gitpod-io/gitpod-sdk-go/internal/apiquery" + "github.com/gitpod-io/gitpod-sdk-go/internal/param" + "github.com/gitpod-io/gitpod-sdk-go/internal/requestconfig" + "github.com/gitpod-io/gitpod-sdk-go/option" + "github.com/gitpod-io/gitpod-sdk-go/packages/pagination" + "github.com/gitpod-io/gitpod-sdk-go/shared" +) + +// GatewayService contains methods and other services that help with interacting +// with the gitpod 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 [NewGatewayService] method instead. +type GatewayService struct { + Options []option.RequestOption +} + +// NewGatewayService 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 NewGatewayService(opts ...option.RequestOption) (r *GatewayService) { + r = &GatewayService{} + r.Options = opts + return +} + +// ListGateways +func (r *GatewayService) List(ctx context.Context, params GatewayListParams, opts ...option.RequestOption) (res *pagination.GatewaysPage[shared.Gateway], err error) { + var raw *http.Response + opts = append(r.Options[:], opts...) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) + path := "gitpod.v1.GatewayService/ListGateways" + cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodPost, path, params, &res, opts...) + if err != nil { + return nil, err + } + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +// ListGateways +func (r *GatewayService) ListAutoPaging(ctx context.Context, params GatewayListParams, opts ...option.RequestOption) *pagination.GatewaysPageAutoPager[shared.Gateway] { + return pagination.NewGatewaysPageAutoPager(r.List(ctx, params, opts...)) +} + +type GatewayListParams struct { + Token param.Field[string] `query:"token"` + PageSize param.Field[int64] `query:"pageSize"` + // pagination contains the pagination options for listing gateways + Pagination param.Field[GatewayListParamsPagination] `json:"pagination"` +} + +func (r GatewayListParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +// URLQuery serializes [GatewayListParams]'s query parameters as `url.Values`. +func (r GatewayListParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatComma, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +// pagination contains the pagination options for listing gateways +type GatewayListParamsPagination struct { + // Token for the next set of results that was returned as next_token of a + // PaginationResponse + Token param.Field[string] `json:"token"` + // Page size is the maximum number of results to retrieve per page. Defaults to 25. + // Maximum 100. + PageSize param.Field[int64] `json:"pageSize"` +} + +func (r GatewayListParamsPagination) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} diff --git a/gateway_test.go b/gateway_test.go new file mode 100644 index 0000000..c2777de --- /dev/null +++ b/gateway_test.go @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package gitpod_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/gitpod-io/gitpod-sdk-go" + "github.com/gitpod-io/gitpod-sdk-go/internal/testutil" + "github.com/gitpod-io/gitpod-sdk-go/option" +) + +func TestGatewayListWithOptionalParams(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 := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + _, err := client.Gateways.List(context.TODO(), gitpod.GatewayListParams{ + Token: gitpod.F("token"), + PageSize: gitpod.F(int64(0)), + Pagination: gitpod.F(gitpod.GatewayListParamsPagination{ + Token: gitpod.F("token"), + PageSize: gitpod.F(int64(100)), + }), + }) + if err != nil { + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/identity.go b/identity.go index b8a642b..3a50be8 100644 --- a/identity.go +++ b/identity.go @@ -116,6 +116,22 @@ func (r *IdentityService) GetIDToken(ctx context.Context, body IdentityGetIDToke return } +type IDTokenVersion string + +const ( + IDTokenVersionUnspecified IDTokenVersion = "ID_TOKEN_VERSION_UNSPECIFIED" + IDTokenVersionV1 IDTokenVersion = "ID_TOKEN_VERSION_V1" + IDTokenVersionV2 IDTokenVersion = "ID_TOKEN_VERSION_V2" +) + +func (r IDTokenVersion) IsKnown() bool { + switch r { + case IDTokenVersionUnspecified, IDTokenVersionV1, IDTokenVersionV2: + return true + } + return false +} + type IdentityExchangeTokenResponse struct { // access_token is the new access token AccessToken string `json:"accessToken"` @@ -202,6 +218,8 @@ func (r IdentityGetAuthenticatedIdentityParams) MarshalJSON() (data []byte, err type IdentityGetIDTokenParams struct { Audience param.Field[[]string] `json:"audience"` + // version is the version of the ID token. + Version param.Field[IDTokenVersion] `json:"version"` } func (r IdentityGetIDTokenParams) MarshalJSON() (data []byte, err error) { diff --git a/identity_test.go b/identity_test.go index 67c6417..f68cf47 100644 --- a/identity_test.go +++ b/identity_test.go @@ -78,6 +78,7 @@ func TestIdentityGetIDTokenWithOptionalParams(t *testing.T) { ) _, err := client.Identity.GetIDToken(context.TODO(), gitpod.IdentityGetIDTokenParams{ Audience: gitpod.F([]string{"https://api.gitpod.io", "https://ws.gitpod.io"}), + Version: gitpod.F(gitpod.IDTokenVersionUnspecified), }) if err != nil { var apierr *gitpod.Error diff --git a/internal/requestconfig/requestconfig.go b/internal/requestconfig/requestconfig.go index c6127df..66ccdd5 100644 --- a/internal/requestconfig/requestconfig.go +++ b/internal/requestconfig/requestconfig.go @@ -10,6 +10,7 @@ import ( "io" "math" "math/rand" + "mime" "net/http" "net/url" "runtime" @@ -21,6 +22,7 @@ import ( "github.com/gitpod-io/gitpod-sdk-go/internal/apierror" "github.com/gitpod-io/gitpod-sdk-go/internal/apiform" "github.com/gitpod-io/gitpod-sdk-go/internal/apiquery" + "github.com/gitpod-io/gitpod-sdk-go/internal/param" ) func getDefaultHeaders() map[string]string { @@ -76,7 +78,17 @@ func getPlatformProperties() map[string]string { } } -func NewRequestConfig(ctx context.Context, method string, u string, body interface{}, dst interface{}, opts ...func(*RequestConfig) error) (*RequestConfig, error) { +type RequestOption interface { + Apply(*RequestConfig) error +} + +type RequestOptionFunc func(*RequestConfig) error +type PreRequestOptionFunc func(*RequestConfig) error + +func (s RequestOptionFunc) Apply(r *RequestConfig) error { return s(r) } +func (s PreRequestOptionFunc) Apply(r *RequestConfig) error { return s(r) } + +func NewRequestConfig(ctx context.Context, method string, u string, body interface{}, dst interface{}, opts ...RequestOption) (*RequestConfig, error) { var reader io.Reader contentType := "application/json" @@ -173,16 +185,33 @@ func NewRequestConfig(ctx context.Context, method string, u string, body interfa return &cfg, nil } +func UseDefaultParam[T any](dst *param.Field[T], src *T) { + if !dst.Present && src != nil { + dst.Value = *src + dst.Present = true + } +} + +// This interface is primarily used to describe an [*http.Client], but also +// supports custom HTTP implementations. +type HTTPDoer interface { + Do(req *http.Request) (*http.Response, error) +} + // RequestConfig represents all the state related to one request. // // Editing the variables inside RequestConfig directly is unstable api. Prefer -// composing func(\*RequestConfig) error instead if possible. +// composing the RequestOption instead if possible. type RequestConfig struct { MaxRetries int RequestTimeout time.Duration Context context.Context Request *http.Request BaseURL *url.URL + // DefaultBaseURL will be used if BaseURL is not explicitly overridden using + // WithBaseURL. + DefaultBaseURL *url.URL + CustomHTTPDoer HTTPDoer HTTPClient *http.Client Middlewares []middleware BearerToken string @@ -222,7 +251,7 @@ func shouldRetry(req *http.Request, res *http.Response) bool { return true } - // If the header explictly wants a retry behavior, respect that over the + // If the header explicitly wants a retry behavior, respect that over the // http status code. if res.Header.Get("x-should-retry") == "true" { return true @@ -348,7 +377,11 @@ func retryDelay(res *http.Response, retryCount int) time.Duration { func (cfg *RequestConfig) Execute() (err error) { if cfg.BaseURL == nil { - return fmt.Errorf("requestconfig: base url is not set") + if cfg.DefaultBaseURL != nil { + cfg.BaseURL = cfg.DefaultBaseURL + } else { + return fmt.Errorf("requestconfig: base url is not set") + } } cfg.Request.URL, err = cfg.BaseURL.Parse(strings.TrimLeft(cfg.Request.URL.String(), "/")) @@ -380,6 +413,9 @@ func (cfg *RequestConfig) Execute() (err error) { } handler := cfg.HTTPClient.Do + if cfg.CustomHTTPDoer != nil { + handler = cfg.CustomHTTPDoer.Do + } for i := len(cfg.Middlewares) - 1; i >= 0; i -= 1 { handler = applyMiddleware(cfg.Middlewares[i], handler) } @@ -479,13 +515,15 @@ func (cfg *RequestConfig) Execute() (err error) { } contents, err := io.ReadAll(res.Body) + res.Body.Close() if err != nil { return fmt.Errorf("error reading response body: %w", err) } // If we are not json, return plaintext contentType := res.Header.Get("content-type") - isJSON := strings.Contains(contentType, "application/json") || strings.Contains(contentType, "application/vnd.api+json") + mediaType, _, _ := mime.ParseMediaType(contentType) + isJSON := strings.Contains(mediaType, "application/json") || strings.HasSuffix(mediaType, "+json") if !isJSON { switch dst := cfg.ResponseBodyInto.(type) { case *string: @@ -496,7 +534,7 @@ func (cfg *RequestConfig) Execute() (err error) { case *[]byte: *dst = contents default: - return fmt.Errorf("expected destination type of 'string' or '[]byte' for responses with content-type that is not 'application/json'") + return fmt.Errorf("expected destination type of 'string' or '[]byte' for responses with content-type '%s' that is not 'application/json'", contentType) } return nil } @@ -515,7 +553,7 @@ func (cfg *RequestConfig) Execute() (err error) { return nil } -func ExecuteNewRequest(ctx context.Context, method string, u string, body interface{}, dst interface{}, opts ...func(*RequestConfig) error) error { +func ExecuteNewRequest(ctx context.Context, method string, u string, body interface{}, dst interface{}, opts ...RequestOption) error { cfg, err := NewRequestConfig(ctx, method, u, body, dst, opts...) if err != nil { return err @@ -549,12 +587,45 @@ func (cfg *RequestConfig) Clone(ctx context.Context) *RequestConfig { return new } -func (cfg *RequestConfig) Apply(opts ...func(*RequestConfig) error) error { +func (cfg *RequestConfig) Apply(opts ...RequestOption) error { for _, opt := range opts { - err := opt(cfg) + err := opt.Apply(cfg) if err != nil { return err } } return nil } + +// PreRequestOptions is used to collect all the options which need to be known before +// a call to [RequestConfig.ExecuteNewRequest], such as path parameters +// or global defaults. +// PreRequestOptions will return a [RequestConfig] with the options applied. +// +// Only request option functions of type [PreRequestOptionFunc] are applied. +func PreRequestOptions(opts ...RequestOption) (RequestConfig, error) { + cfg := RequestConfig{} + for _, opt := range opts { + if opt, ok := opt.(PreRequestOptionFunc); ok { + err := opt.Apply(&cfg) + if err != nil { + return cfg, err + } + } + } + return cfg, nil +} + +// WithDefaultBaseURL returns a RequestOption that sets the client's default Base URL. +// This is always overridden by setting a base URL with WithBaseURL. +// WithBaseURL should be used instead of WithDefaultBaseURL except in internal code. +func WithDefaultBaseURL(baseURL string) RequestOption { + u, err := url.Parse(baseURL) + return RequestOptionFunc(func(r *RequestConfig) error { + if err != nil { + return err + } + r.DefaultBaseURL = u + return nil + }) +} diff --git a/internal/version.go b/internal/version.go index 5c62cac..67c4d40 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.4.0" // x-release-please-version +const PackageVersion = "0.5.0" // x-release-please-version diff --git a/option/requestoption.go b/option/requestoption.go index 05868a8..a02cd87 100644 --- a/option/requestoption.go +++ b/option/requestoption.go @@ -6,9 +6,9 @@ import ( "bytes" "fmt" "io" - "log" "net/http" "net/url" + "strings" "time" "github.com/gitpod-io/gitpod-sdk-go/internal/requestconfig" @@ -20,27 +20,56 @@ import ( // options pattern in our [README]. // // [README]: https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#readme-requestoptions -type RequestOption = func(*requestconfig.RequestConfig) error +type RequestOption = requestconfig.RequestOption // WithBaseURL returns a RequestOption that sets the BaseURL for the client. +// +// For security reasons, ensure that the base URL is trusted. func WithBaseURL(base string) RequestOption { u, err := url.Parse(base) - if err != nil { - log.Fatalf("failed to parse BaseURL: %s\n", err) - } - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { + if err != nil { + return fmt.Errorf("requestoption: WithBaseURL failed to parse url %s\n", err) + } + + if u.Path != "" && !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } r.BaseURL = u return nil - } + }) } -// WithHTTPClient returns a RequestOption that changes the underlying [http.Client] used to make this +// HTTPClient is primarily used to describe an [*http.Client], but also +// supports custom implementations. +// +// For bespoke implementations, prefer using an [*http.Client] with a +// custom transport. See [http.RoundTripper] for further information. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// WithHTTPClient returns a RequestOption that changes the underlying http client used to make this // request, which by default is [http.DefaultClient]. -func WithHTTPClient(client *http.Client) RequestOption { - return func(r *requestconfig.RequestConfig) error { - r.HTTPClient = client +// +// For custom uses cases, it is recommended to provide an [*http.Client] with a custom +// [http.RoundTripper] as its transport, rather than directly implementing [HTTPClient]. +func WithHTTPClient(client HTTPClient) RequestOption { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { + if client == nil { + return fmt.Errorf("requestoption: custom http client cannot be nil") + } + + if c, ok := client.(*http.Client); ok { + // Prefer the native client if possible. + r.HTTPClient = c + r.CustomHTTPDoer = nil + } else { + r.CustomHTTPDoer = client + } + return nil - } + }) } // MiddlewareNext is a function which is called by a middleware to pass an HTTP request @@ -55,10 +84,10 @@ type Middleware = func(*http.Request, MiddlewareNext) (*http.Response, error) // WithMiddleware returns a RequestOption that applies the given middleware // to the requests made. Each middleware will execute in the order they were given. func WithMiddleware(middlewares ...Middleware) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.Middlewares = append(r.Middlewares, middlewares...) return nil - } + }) } // WithMaxRetries returns a RequestOption that sets the maximum number of retries that the client @@ -70,68 +99,68 @@ func WithMaxRetries(retries int) RequestOption { if retries < 0 { panic("option: cannot have fewer than 0 retries") } - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.MaxRetries = retries return nil - } + }) } // WithHeader returns a RequestOption that sets the header value to the associated key. It overwrites // any value if there was one already present. func WithHeader(key, value string) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.Request.Header.Set(key, value) return nil - } + }) } // WithHeaderAdd returns a RequestOption that adds the header value to the associated key. It appends // onto any existing values. func WithHeaderAdd(key, value string) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.Request.Header.Add(key, value) return nil - } + }) } // WithHeaderDel returns a RequestOption that deletes the header value(s) associated with the given key. func WithHeaderDel(key string) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.Request.Header.Del(key) return nil - } + }) } // WithQuery returns a RequestOption that sets the query value to the associated key. It overwrites // any value if there was one already present. func WithQuery(key, value string) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { query := r.Request.URL.Query() query.Set(key, value) r.Request.URL.RawQuery = query.Encode() return nil - } + }) } // WithQueryAdd returns a RequestOption that adds the query value to the associated key. It appends // onto any existing values. func WithQueryAdd(key, value string) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { query := r.Request.URL.Query() query.Add(key, value) r.Request.URL.RawQuery = query.Encode() return nil - } + }) } // WithQueryDel returns a RequestOption that deletes the query value(s) associated with the key. func WithQueryDel(key string) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { query := r.Request.URL.Query() query.Del(key) r.Request.URL.RawQuery = query.Encode() return nil - } + }) } // WithJSONSet returns a RequestOption that sets the body's JSON value associated with the key. @@ -139,19 +168,27 @@ func WithQueryDel(key string) RequestOption { // // [sjson format]: https://github.com/tidwall/sjson func WithJSONSet(key string, value interface{}) RequestOption { - return func(r *requestconfig.RequestConfig) (err error) { - if buffer, ok := r.Body.(*bytes.Buffer); ok { - b := buffer.Bytes() + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) (err error) { + var b []byte + + if r.Body == nil { + b, err = sjson.SetBytes(nil, key, value) + if err != nil { + return err + } + } else if buffer, ok := r.Body.(*bytes.Buffer); ok { + b = buffer.Bytes() b, err = sjson.SetBytes(b, key, value) if err != nil { return err } - r.Body = bytes.NewBuffer(b) - return nil + } else { + return fmt.Errorf("cannot use WithJSONSet on a body that is not serialized as *bytes.Buffer") } - return fmt.Errorf("cannot use WithJSONSet on a body that is not serialized as *bytes.Buffer") - } + r.Body = bytes.NewBuffer(b) + return nil + }) } // WithJSONDel returns a RequestOption that deletes the body's JSON value associated with the key. @@ -159,7 +196,7 @@ func WithJSONSet(key string, value interface{}) RequestOption { // // [sjson format]: https://github.com/tidwall/sjson func WithJSONDel(key string) RequestOption { - return func(r *requestconfig.RequestConfig) (err error) { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) (err error) { if buffer, ok := r.Body.(*bytes.Buffer); ok { b := buffer.Bytes() b, err = sjson.DeleteBytes(b, key) @@ -171,24 +208,24 @@ func WithJSONDel(key string) RequestOption { } return fmt.Errorf("cannot use WithJSONDel on a body that is not serialized as *bytes.Buffer") - } + }) } // WithResponseBodyInto returns a RequestOption that overwrites the deserialization target with // the given destination. If provided, we don't deserialize into the default struct. func WithResponseBodyInto(dst any) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.ResponseBodyInto = dst return nil - } + }) } // WithResponseInto returns a RequestOption that copies the [*http.Response] into the given address. func WithResponseInto(dst **http.Response) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.ResponseInto = dst return nil - } + }) } // WithRequestBody returns a RequestOption that provides a custom serialized body with the given @@ -196,7 +233,7 @@ func WithResponseInto(dst **http.Response) RequestOption { // // body accepts an io.Reader or raw []bytes. func WithRequestBody(contentType string, body any) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { if reader, ok := body.(io.Reader); ok { r.Body = reader return r.Apply(WithHeader("Content-Type", contentType)) @@ -208,30 +245,30 @@ func WithRequestBody(contentType string, body any) RequestOption { } return fmt.Errorf("body must be a byte slice or implement io.Reader") - } + }) } // WithRequestTimeout returns a RequestOption that sets the timeout for // each request attempt. This should be smaller than the timeout defined in // the context, which spans all retries. func WithRequestTimeout(dur time.Duration) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.RequestTimeout = dur return nil - } + }) } // WithEnvironmentProduction returns a RequestOption that sets the current // environment to be the "production" environment. An environment specifies which base URL // to use by default. func WithEnvironmentProduction() RequestOption { - return WithBaseURL("https://app.gitpod.io/api/") + return requestconfig.WithDefaultBaseURL("https://app.gitpod.io/api/") } // WithBearerToken returns a RequestOption that sets the client setting "bearer_token". func WithBearerToken(value string) RequestOption { - return func(r *requestconfig.RequestConfig) error { + return requestconfig.RequestOptionFunc(func(r *requestconfig.RequestConfig) error { r.BearerToken = value return r.Apply(WithHeader("authorization", fmt.Sprintf("Bearer %s", r.BearerToken))) - } + }) } diff --git a/organization.go b/organization.go index 41dca1b..4a493dd 100644 --- a/organization.go +++ b/organization.go @@ -27,6 +27,7 @@ type OrganizationService struct { Options []option.RequestOption DomainVerifications *OrganizationDomainVerificationService Invites *OrganizationInviteService + Policies *OrganizationPolicyService SSOConfigurations *OrganizationSSOConfigurationService } @@ -38,6 +39,7 @@ func NewOrganizationService(opts ...option.RequestOption) (r *OrganizationServic r.Options = opts r.DomainVerifications = NewOrganizationDomainVerificationService(opts...) r.Invites = NewOrganizationInviteService(opts...) + r.Policies = NewOrganizationPolicyService(opts...) r.SSOConfigurations = NewOrganizationSSOConfigurationService(opts...) return } @@ -143,85 +145,6 @@ func (r *OrganizationService) Update(ctx context.Context, body OrganizationUpdat return } -// Lists all organizations the caller has access to with optional filtering. -// -// Use this method to: -// -// - View organizations you're a member of -// - Browse all available organizations -// - Paginate through organization results -// -// ### Examples -// -// - List member organizations: -// -// Shows organizations where the caller is a member. -// -// ```yaml -// pagination: -// pageSize: 20 -// scope: SCOPE_MEMBER -// ``` -// -// - List all organizations: -// -// Shows all organizations visible to the caller. -// -// ```yaml -// pagination: -// pageSize: 50 -// scope: SCOPE_ALL -// ``` -func (r *OrganizationService) List(ctx context.Context, params OrganizationListParams, opts ...option.RequestOption) (res *pagination.OrganizationsPage[Organization], err error) { - var raw *http.Response - opts = append(r.Options[:], opts...) - opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) - path := "gitpod.v1.OrganizationService/ListOrganizations" - cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodPost, path, params, &res, opts...) - if err != nil { - return nil, err - } - err = cfg.Execute() - if err != nil { - return nil, err - } - res.SetPageConfig(cfg, raw) - return res, nil -} - -// Lists all organizations the caller has access to with optional filtering. -// -// Use this method to: -// -// - View organizations you're a member of -// - Browse all available organizations -// - Paginate through organization results -// -// ### Examples -// -// - List member organizations: -// -// Shows organizations where the caller is a member. -// -// ```yaml -// pagination: -// pageSize: 20 -// scope: SCOPE_MEMBER -// ``` -// -// - List all organizations: -// -// Shows all organizations visible to the caller. -// -// ```yaml -// pagination: -// pageSize: 50 -// scope: SCOPE_ALL -// ``` -func (r *OrganizationService) ListAutoPaging(ctx context.Context, params OrganizationListParams, opts ...option.RequestOption) *pagination.OrganizationsPageAutoPager[Organization] { - return pagination.NewOrganizationsPageAutoPager(r.List(ctx, params, opts...)) -} - // Permanently deletes an organization. // // Use this method to: @@ -546,6 +469,8 @@ type Organization struct { // to obtain a formatter capable of generating timestamps in this format. CreatedAt time.Time `json:"createdAt,required" format:"date-time"` Name string `json:"name,required"` + // The tier of the organization - free or enterprise + Tier OrganizationTier `json:"tier,required"` // A Timestamp represents a point in time independent of any time zone or local // calendar, encoded as a count of seconds and fractions of seconds at nanosecond // resolution. The count is relative to an epoch at UTC midnight on January 1, @@ -644,6 +569,7 @@ type organizationJSON struct { ID apijson.Field CreatedAt apijson.Field Name apijson.Field + Tier apijson.Field UpdatedAt apijson.Field InviteDomains apijson.Field raw string @@ -782,17 +708,17 @@ func (r organizationMemberJSON) RawJSON() string { return r.raw } -type Scope string +type OrganizationTier string const ( - ScopeUnspecified Scope = "SCOPE_UNSPECIFIED" - ScopeMember Scope = "SCOPE_MEMBER" - ScopeAll Scope = "SCOPE_ALL" + OrganizationTierUnspecified OrganizationTier = "ORGANIZATION_TIER_UNSPECIFIED" + OrganizationTierFree OrganizationTier = "ORGANIZATION_TIER_FREE" + OrganizationTierEnterprise OrganizationTier = "ORGANIZATION_TIER_ENTERPRISE" ) -func (r Scope) IsKnown() bool { +func (r OrganizationTier) IsKnown() bool { switch r { - case ScopeUnspecified, ScopeMember, ScopeAll: + case OrganizationTierUnspecified, OrganizationTierFree, OrganizationTierEnterprise: return true } return false @@ -933,41 +859,6 @@ func (r OrganizationUpdateParams) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } -type OrganizationListParams struct { - Token param.Field[string] `query:"token"` - PageSize param.Field[int64] `query:"pageSize"` - // pagination contains the pagination options for listing organizations - Pagination param.Field[OrganizationListParamsPagination] `json:"pagination"` - // scope is the scope of the organizations to list - Scope param.Field[Scope] `json:"scope"` -} - -func (r OrganizationListParams) MarshalJSON() (data []byte, err error) { - return apijson.MarshalRoot(r) -} - -// URLQuery serializes [OrganizationListParams]'s query parameters as `url.Values`. -func (r OrganizationListParams) URLQuery() (v url.Values) { - return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ - ArrayFormat: apiquery.ArrayQueryFormatComma, - NestedFormat: apiquery.NestedQueryFormatBrackets, - }) -} - -// pagination contains the pagination options for listing organizations -type OrganizationListParamsPagination struct { - // Token for the next set of results that was returned as next_token of a - // PaginationResponse - Token param.Field[string] `json:"token"` - // Page size is the maximum number of results to retrieve per page. Defaults to 25. - // Maximum 100. - PageSize param.Field[int64] `json:"pageSize"` -} - -func (r OrganizationListParamsPagination) MarshalJSON() (data []byte, err error) { - return apijson.MarshalRoot(r) -} - type OrganizationDeleteParams struct { // organization_id is the ID of the organization to delete OrganizationID param.Field[string] `json:"organizationId,required" format:"uuid"` diff --git a/organization_test.go b/organization_test.go index 2a28e90..97436c9 100644 --- a/organization_test.go +++ b/organization_test.go @@ -95,37 +95,6 @@ func TestOrganizationUpdateWithOptionalParams(t *testing.T) { } } -func TestOrganizationListWithOptionalParams(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 := gitpod.NewClient( - option.WithBaseURL(baseURL), - option.WithBearerToken("My Bearer Token"), - ) - _, err := client.Organizations.List(context.TODO(), gitpod.OrganizationListParams{ - Token: gitpod.F("token"), - PageSize: gitpod.F(int64(0)), - Pagination: gitpod.F(gitpod.OrganizationListParamsPagination{ - Token: gitpod.F("token"), - PageSize: gitpod.F(int64(50)), - }), - Scope: gitpod.F(gitpod.ScopeUnspecified), - }) - if err != nil { - var apierr *gitpod.Error - if errors.As(err, &apierr) { - t.Log(string(apierr.DumpRequest(true))) - } - t.Fatalf("err should be nil: %s", err.Error()) - } -} - func TestOrganizationDelete(t *testing.T) { t.Skip("skipped: tests are disabled for the time being") baseURL := "http://localhost:4010" @@ -249,7 +218,7 @@ func TestOrganizationSetRoleWithOptionalParams(t *testing.T) { _, err := client.Organizations.SetRole(context.TODO(), gitpod.OrganizationSetRoleParams{ OrganizationID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), UserID: gitpod.F("f53d2330-3795-4c5d-a1f3-453121af9c60"), - Role: gitpod.F(shared.OrganizationRoleUnspecified), + Role: gitpod.F(shared.OrganizationRoleMember), }) if err != nil { var apierr *gitpod.Error diff --git a/organizationpolicy.go b/organizationpolicy.go new file mode 100644 index 0000000..1572d7f --- /dev/null +++ b/organizationpolicy.go @@ -0,0 +1,229 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package gitpod + +import ( + "context" + "net/http" + + "github.com/gitpod-io/gitpod-sdk-go/internal/apijson" + "github.com/gitpod-io/gitpod-sdk-go/internal/param" + "github.com/gitpod-io/gitpod-sdk-go/internal/requestconfig" + "github.com/gitpod-io/gitpod-sdk-go/option" +) + +// OrganizationPolicyService contains methods and other services that help with +// interacting with the gitpod 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 [NewOrganizationPolicyService] method instead. +type OrganizationPolicyService struct { + Options []option.RequestOption +} + +// NewOrganizationPolicyService 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 NewOrganizationPolicyService(opts ...option.RequestOption) (r *OrganizationPolicyService) { + r = &OrganizationPolicyService{} + r.Options = opts + return +} + +// Gets organization policy settings by organization ID. +// +// Use this method to: +// +// - Retrieve current policy settings for an organization +// - View resource limits and restrictions +// - Check allowed editors and other configurations +// +// ### Examples +// +// - Get organization policies: +// +// Retrieves policy settings for a specific organization. +// +// ```yaml +// organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047" +// ``` +func (r *OrganizationPolicyService) Get(ctx context.Context, body OrganizationPolicyGetParams, opts ...option.RequestOption) (res *OrganizationPolicyGetResponse, err error) { + opts = append(r.Options[:], opts...) + path := "gitpod.v1.OrganizationService/GetOrganizationPolicies" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Updates organization policy settings. +// +// Use this method to: +// +// - Configure editor restrictions +// - Set environment resource limits +// - Define project creation permissions +// - Customize default configurations +// +// ### Examples +// +// - Update editor policies: +// +// Restricts available editors and sets a default. +// +// ```yaml +// organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047" +// allowedEditorIds: +// - "vscode" +// - "jetbrains" +// defaultEditorId: "vscode" +// ``` +// +// - Set environment limits: +// +// Configures limits for environment usage. +// +// ```yaml +// organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047" +// maximumEnvironmentTimeout: "3600s" +// maximumRunningEnvironmentsPerUser: "5" +// maximumEnvironmentsPerUser: "20" +// ``` +func (r *OrganizationPolicyService) Update(ctx context.Context, body OrganizationPolicyUpdateParams, opts ...option.RequestOption) (res *OrganizationPolicyUpdateResponse, err error) { + opts = append(r.Options[:], opts...) + path := "gitpod.v1.OrganizationService/UpdateOrganizationPolicies" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +type OrganizationPolicies struct { + // allowed_editor_ids is the list of editor IDs that are allowed to be used in the + // organization + AllowedEditorIDs []string `json:"allowedEditorIds,required"` + // allow_local_runners controls whether local runners are allowed to be used in the + // organization + AllowLocalRunners bool `json:"allowLocalRunners,required"` + // default_editor_id is the default editor ID to be used when a user doesn't + // specify one + DefaultEditorID string `json:"defaultEditorId,required"` + // default_environment_image is the default container image when none is defined in + // repo + DefaultEnvironmentImage string `json:"defaultEnvironmentImage,required"` + // maximum_environments_per_user limits total environments (running or stopped) per + // user + MaximumEnvironmentsPerUser string `json:"maximumEnvironmentsPerUser,required"` + // maximum_running_environments_per_user limits simultaneously running environments + // per user + MaximumRunningEnvironmentsPerUser string `json:"maximumRunningEnvironmentsPerUser,required"` + // members_create_projects controls whether members can create projects + MembersCreateProjects bool `json:"membersCreateProjects,required"` + // members_require_projects controls whether environments can only be created from + // projects by non-admin users + MembersRequireProjects bool `json:"membersRequireProjects,required"` + // organization_id is the ID of the organization + OrganizationID string `json:"organizationId,required" format:"uuid"` + // port_sharing_disabled controls whether port sharing is disabled in the + // organization + PortSharingDisabled bool `json:"portSharingDisabled,required"` + // maximum_environment_timeout controls the maximum timeout allowed for + // environments in seconds. 0 means no limit (never). Minimum duration is 30 + // minutes. + MaximumEnvironmentTimeout string `json:"maximumEnvironmentTimeout" format:"regex"` + JSON organizationPoliciesJSON `json:"-"` +} + +// organizationPoliciesJSON contains the JSON metadata for the struct +// [OrganizationPolicies] +type organizationPoliciesJSON struct { + AllowedEditorIDs apijson.Field + AllowLocalRunners apijson.Field + DefaultEditorID apijson.Field + DefaultEnvironmentImage apijson.Field + MaximumEnvironmentsPerUser apijson.Field + MaximumRunningEnvironmentsPerUser apijson.Field + MembersCreateProjects apijson.Field + MembersRequireProjects apijson.Field + OrganizationID apijson.Field + PortSharingDisabled apijson.Field + MaximumEnvironmentTimeout apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *OrganizationPolicies) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r organizationPoliciesJSON) RawJSON() string { + return r.raw +} + +type OrganizationPolicyGetResponse struct { + Policies OrganizationPolicies `json:"policies,required"` + JSON organizationPolicyGetResponseJSON `json:"-"` +} + +// organizationPolicyGetResponseJSON contains the JSON metadata for the struct +// [OrganizationPolicyGetResponse] +type organizationPolicyGetResponseJSON struct { + Policies apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *OrganizationPolicyGetResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r organizationPolicyGetResponseJSON) RawJSON() string { + return r.raw +} + +type OrganizationPolicyUpdateResponse = interface{} + +type OrganizationPolicyGetParams struct { + // organization_id is the ID of the organization to retrieve policies for + OrganizationID param.Field[string] `json:"organizationId,required" format:"uuid"` +} + +func (r OrganizationPolicyGetParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +type OrganizationPolicyUpdateParams struct { + // organization_id is the ID of the organization to update policies for + OrganizationID param.Field[string] `json:"organizationId,required" format:"uuid"` + // allowed_editor_ids is the list of editor IDs that are allowed to be used in the + // organization + AllowedEditorIDs param.Field[[]string] `json:"allowedEditorIds"` + // allow_local_runners controls whether local runners are allowed to be used in the + // organization + AllowLocalRunners param.Field[bool] `json:"allowLocalRunners"` + // default_editor_id is the default editor ID to be used when a user doesn't + // specify one + DefaultEditorID param.Field[string] `json:"defaultEditorId"` + // default_environment_image is the default container image when none is defined in + // repo + DefaultEnvironmentImage param.Field[string] `json:"defaultEnvironmentImage"` + // maximum_environments_per_user limits total environments (running or stopped) per + // user + MaximumEnvironmentsPerUser param.Field[string] `json:"maximumEnvironmentsPerUser"` + // maximum_environment_timeout controls the maximum timeout allowed for + // environments in seconds. 0 means no limit (never). Minimum duration is 30 + // minutes. + MaximumEnvironmentTimeout param.Field[string] `json:"maximumEnvironmentTimeout" format:"regex"` + // maximum_running_environments_per_user limits simultaneously running environments + // per user + MaximumRunningEnvironmentsPerUser param.Field[string] `json:"maximumRunningEnvironmentsPerUser"` + // members_create_projects controls whether members can create projects + MembersCreateProjects param.Field[bool] `json:"membersCreateProjects"` + // members_require_projects controls whether environments can only be created from + // projects by non-admin users + MembersRequireProjects param.Field[bool] `json:"membersRequireProjects"` + // port_sharing_disabled controls whether port sharing is disabled in the + // organization + PortSharingDisabled param.Field[bool] `json:"portSharingDisabled"` +} + +func (r OrganizationPolicyUpdateParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} diff --git a/organizationpolicy_test.go b/organizationpolicy_test.go new file mode 100644 index 0000000..6f7c167 --- /dev/null +++ b/organizationpolicy_test.go @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package gitpod_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/gitpod-io/gitpod-sdk-go" + "github.com/gitpod-io/gitpod-sdk-go/internal/testutil" + "github.com/gitpod-io/gitpod-sdk-go/option" +) + +func TestOrganizationPolicyGet(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 := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + _, err := client.Organizations.Policies.Get(context.TODO(), gitpod.OrganizationPolicyGetParams{ + OrganizationID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), + }) + if err != nil { + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestOrganizationPolicyUpdateWithOptionalParams(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 := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + _, err := client.Organizations.Policies.Update(context.TODO(), gitpod.OrganizationPolicyUpdateParams{ + OrganizationID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), + AllowedEditorIDs: gitpod.F([]string{"string"}), + AllowLocalRunners: gitpod.F(true), + DefaultEditorID: gitpod.F("defaultEditorId"), + DefaultEnvironmentImage: gitpod.F("defaultEnvironmentImage"), + MaximumEnvironmentsPerUser: gitpod.F("20"), + MaximumEnvironmentTimeout: gitpod.F("3600s"), + MaximumRunningEnvironmentsPerUser: gitpod.F("5"), + MembersCreateProjects: gitpod.F(true), + MembersRequireProjects: gitpod.F(true), + PortSharingDisabled: gitpod.F(true), + }) + if err != nil { + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/packages/jsonl/jsonl.go b/packages/jsonl/jsonl.go index 80c89b9..95f8a2a 100644 --- a/packages/jsonl/jsonl.go +++ b/packages/jsonl/jsonl.go @@ -38,7 +38,9 @@ func (s *Stream[T]) Next() bool { } line := s.scn.Bytes() - s.err = json.Unmarshal(line, &s.cur) + var nxt T + s.err = json.Unmarshal(line, &nxt) + s.cur = nxt return s.err == nil } diff --git a/packages/pagination/pagination.go b/packages/pagination/pagination.go index 03d33c5..ad46709 100644 --- a/packages/pagination/pagination.go +++ b/packages/pagination/pagination.go @@ -65,7 +65,10 @@ func (r *DomainVerificationsPage[T]) GetNextPage() (res *DomainVerificationsPage return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -183,7 +186,10 @@ func (r *EditorsPage[T]) GetNextPage() (res *EditorsPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -301,7 +307,10 @@ func (r *EntriesPage[T]) GetNextPage() (res *EntriesPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -420,7 +429,10 @@ func (r *EnvironmentClassesPage[T]) GetNextPage() (res *EnvironmentClassesPage[T return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -539,7 +551,10 @@ func (r *EnvironmentsPage[T]) GetNextPage() (res *EnvironmentsPage[T], err error return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -603,6 +618,127 @@ func (r *EnvironmentsPageAutoPager[T]) Index() int { return r.run } +type GatewaysPagePagination struct { + NextToken string `json:"nextToken"` + JSON gatewaysPagePaginationJSON `json:"-"` +} + +// gatewaysPagePaginationJSON contains the JSON metadata for the struct +// [GatewaysPagePagination] +type gatewaysPagePaginationJSON struct { + NextToken apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *GatewaysPagePagination) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r gatewaysPagePaginationJSON) RawJSON() string { + return r.raw +} + +type GatewaysPage[T any] struct { + Gateways []T `json:"gateways"` + Pagination GatewaysPagePagination `json:"pagination"` + JSON gatewaysPageJSON `json:"-"` + cfg *requestconfig.RequestConfig + res *http.Response +} + +// gatewaysPageJSON contains the JSON metadata for the struct [GatewaysPage[T]] +type gatewaysPageJSON struct { + Gateways apijson.Field + Pagination apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *GatewaysPage[T]) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r gatewaysPageJSON) RawJSON() string { + return r.raw +} + +// GetNextPage returns the next page as defined by this pagination style. When +// there is no next page, this function will return a 'nil' for the page value, but +// will not return an error +func (r *GatewaysPage[T]) GetNextPage() (res *GatewaysPage[T], err error) { + next := r.Pagination.NextToken + if len(next) == 0 { + return nil, nil + } + cfg := r.cfg.Clone(r.cfg.Context) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } + var raw *http.Response + cfg.ResponseInto = &raw + cfg.ResponseBodyInto = &res + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +func (r *GatewaysPage[T]) SetPageConfig(cfg *requestconfig.RequestConfig, res *http.Response) { + if r == nil { + r = &GatewaysPage[T]{} + } + r.cfg = cfg + r.res = res +} + +type GatewaysPageAutoPager[T any] struct { + page *GatewaysPage[T] + cur T + idx int + run int + err error +} + +func NewGatewaysPageAutoPager[T any](page *GatewaysPage[T], err error) *GatewaysPageAutoPager[T] { + return &GatewaysPageAutoPager[T]{ + page: page, + err: err, + } +} + +func (r *GatewaysPageAutoPager[T]) Next() bool { + if r.page == nil || len(r.page.Gateways) == 0 { + return false + } + if r.idx >= len(r.page.Gateways) { + r.idx = 0 + r.page, r.err = r.page.GetNextPage() + if r.err != nil || r.page == nil || len(r.page.Gateways) == 0 { + return false + } + } + r.cur = r.page.Gateways[r.idx] + r.run += 1 + r.idx += 1 + return true +} + +func (r *GatewaysPageAutoPager[T]) Current() T { + return r.cur +} + +func (r *GatewaysPageAutoPager[T]) Err() error { + return r.err +} + +func (r *GatewaysPageAutoPager[T]) Index() int { + return r.run +} + type GroupsPagePagination struct { NextToken string `json:"nextToken"` JSON groupsPagePaginationJSON `json:"-"` @@ -657,7 +793,10 @@ func (r *GroupsPage[T]) GetNextPage() (res *GroupsPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -776,7 +915,10 @@ func (r *IntegrationsPage[T]) GetNextPage() (res *IntegrationsPage[T], err error return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -895,7 +1037,10 @@ func (r *LoginProvidersPage[T]) GetNextPage() (res *LoginProvidersPage[T], err e return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -1013,7 +1158,10 @@ func (r *MembersPage[T]) GetNextPage() (res *MembersPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -1077,125 +1225,6 @@ func (r *MembersPageAutoPager[T]) Index() int { return r.run } -type OrganizationsPagePagination struct { - NextToken string `json:"nextToken"` - JSON organizationsPagePaginationJSON `json:"-"` -} - -// organizationsPagePaginationJSON contains the JSON metadata for the struct -// [OrganizationsPagePagination] -type organizationsPagePaginationJSON struct { - NextToken apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *OrganizationsPagePagination) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - -func (r organizationsPagePaginationJSON) RawJSON() string { - return r.raw -} - -type OrganizationsPage[T any] struct { - Organizations []T `json:"organizations"` - Pagination OrganizationsPagePagination `json:"pagination"` - JSON organizationsPageJSON `json:"-"` - cfg *requestconfig.RequestConfig - res *http.Response -} - -// organizationsPageJSON contains the JSON metadata for the struct -// [OrganizationsPage[T]] -type organizationsPageJSON struct { - Organizations apijson.Field - Pagination apijson.Field - raw string - ExtraFields map[string]apijson.Field -} - -func (r *OrganizationsPage[T]) UnmarshalJSON(data []byte) (err error) { - return apijson.UnmarshalRoot(data, r) -} - -func (r organizationsPageJSON) RawJSON() string { - return r.raw -} - -// GetNextPage returns the next page as defined by this pagination style. When -// there is no next page, this function will return a 'nil' for the page value, but -// will not return an error -func (r *OrganizationsPage[T]) GetNextPage() (res *OrganizationsPage[T], err error) { - next := r.Pagination.NextToken - if len(next) == 0 { - return nil, nil - } - cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) - var raw *http.Response - cfg.ResponseInto = &raw - cfg.ResponseBodyInto = &res - err = cfg.Execute() - if err != nil { - return nil, err - } - res.SetPageConfig(cfg, raw) - return res, nil -} - -func (r *OrganizationsPage[T]) SetPageConfig(cfg *requestconfig.RequestConfig, res *http.Response) { - if r == nil { - r = &OrganizationsPage[T]{} - } - r.cfg = cfg - r.res = res -} - -type OrganizationsPageAutoPager[T any] struct { - page *OrganizationsPage[T] - cur T - idx int - run int - err error -} - -func NewOrganizationsPageAutoPager[T any](page *OrganizationsPage[T], err error) *OrganizationsPageAutoPager[T] { - return &OrganizationsPageAutoPager[T]{ - page: page, - err: err, - } -} - -func (r *OrganizationsPageAutoPager[T]) Next() bool { - if r.page == nil || len(r.page.Organizations) == 0 { - return false - } - if r.idx >= len(r.page.Organizations) { - r.idx = 0 - r.page, r.err = r.page.GetNextPage() - if r.err != nil || r.page == nil || len(r.page.Organizations) == 0 { - return false - } - } - r.cur = r.page.Organizations[r.idx] - r.run += 1 - r.idx += 1 - return true -} - -func (r *OrganizationsPageAutoPager[T]) Current() T { - return r.cur -} - -func (r *OrganizationsPageAutoPager[T]) Err() error { - return r.err -} - -func (r *OrganizationsPageAutoPager[T]) Index() int { - return r.run -} - type PersonalAccessTokensPagePagination struct { NextToken string `json:"nextToken"` JSON personalAccessTokensPagePaginationJSON `json:"-"` @@ -1251,7 +1280,10 @@ func (r *PersonalAccessTokensPage[T]) GetNextPage() (res *PersonalAccessTokensPa return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -1369,7 +1401,10 @@ func (r *PoliciesPage[T]) GetNextPage() (res *PoliciesPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -1487,7 +1522,10 @@ func (r *ProjectsPage[T]) GetNextPage() (res *ProjectsPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -1551,6 +1589,127 @@ func (r *ProjectsPageAutoPager[T]) Index() int { return r.run } +type RecordsPagePagination struct { + NextToken string `json:"nextToken"` + JSON recordsPagePaginationJSON `json:"-"` +} + +// recordsPagePaginationJSON contains the JSON metadata for the struct +// [RecordsPagePagination] +type recordsPagePaginationJSON struct { + NextToken apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *RecordsPagePagination) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r recordsPagePaginationJSON) RawJSON() string { + return r.raw +} + +type RecordsPage[T any] struct { + Pagination RecordsPagePagination `json:"pagination"` + Records []T `json:"records"` + JSON recordsPageJSON `json:"-"` + cfg *requestconfig.RequestConfig + res *http.Response +} + +// recordsPageJSON contains the JSON metadata for the struct [RecordsPage[T]] +type recordsPageJSON struct { + Pagination apijson.Field + Records apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *RecordsPage[T]) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r recordsPageJSON) RawJSON() string { + return r.raw +} + +// GetNextPage returns the next page as defined by this pagination style. When +// there is no next page, this function will return a 'nil' for the page value, but +// will not return an error +func (r *RecordsPage[T]) GetNextPage() (res *RecordsPage[T], err error) { + next := r.Pagination.NextToken + if len(next) == 0 { + return nil, nil + } + cfg := r.cfg.Clone(r.cfg.Context) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } + var raw *http.Response + cfg.ResponseInto = &raw + cfg.ResponseBodyInto = &res + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +func (r *RecordsPage[T]) SetPageConfig(cfg *requestconfig.RequestConfig, res *http.Response) { + if r == nil { + r = &RecordsPage[T]{} + } + r.cfg = cfg + r.res = res +} + +type RecordsPageAutoPager[T any] struct { + page *RecordsPage[T] + cur T + idx int + run int + err error +} + +func NewRecordsPageAutoPager[T any](page *RecordsPage[T], err error) *RecordsPageAutoPager[T] { + return &RecordsPageAutoPager[T]{ + page: page, + err: err, + } +} + +func (r *RecordsPageAutoPager[T]) Next() bool { + if r.page == nil || len(r.page.Records) == 0 { + return false + } + if r.idx >= len(r.page.Records) { + r.idx = 0 + r.page, r.err = r.page.GetNextPage() + if r.err != nil || r.page == nil || len(r.page.Records) == 0 { + return false + } + } + r.cur = r.page.Records[r.idx] + r.run += 1 + r.idx += 1 + return true +} + +func (r *RecordsPageAutoPager[T]) Current() T { + return r.cur +} + +func (r *RecordsPageAutoPager[T]) Err() error { + return r.err +} + +func (r *RecordsPageAutoPager[T]) Index() int { + return r.run +} + type RunnersPagePagination struct { NextToken string `json:"nextToken"` JSON runnersPagePaginationJSON `json:"-"` @@ -1605,7 +1764,10 @@ func (r *RunnersPage[T]) GetNextPage() (res *RunnersPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -1723,7 +1885,10 @@ func (r *SecretsPage[T]) GetNextPage() (res *SecretsPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -1841,7 +2006,10 @@ func (r *ServicesPage[T]) GetNextPage() (res *ServicesPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -1960,7 +2128,10 @@ func (r *SSOConfigurationsPage[T]) GetNextPage() (res *SSOConfigurationsPage[T], return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -2079,7 +2250,10 @@ func (r *TaskExecutionsPage[T]) GetNextPage() (res *TaskExecutionsPage[T], err e return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -2197,7 +2371,10 @@ func (r *TasksPage[T]) GetNextPage() (res *TasksPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res @@ -2315,7 +2492,10 @@ func (r *TokensPage[T]) GetNextPage() (res *TokensPage[T], err error) { return nil, nil } cfg := r.cfg.Clone(r.cfg.Context) - cfg.Apply(option.WithQuery("token", next)) + err = cfg.Apply(option.WithQuery("token", next)) + if err != nil { + return nil, err + } var raw *http.Response cfg.ResponseInto = &raw cfg.ResponseBodyInto = &res diff --git a/project.go b/project.go index f3055fa..f589536 100644 --- a/project.go +++ b/project.go @@ -428,8 +428,11 @@ type Project struct { // initializer is the content initializer Initializer EnvironmentInitializer `json:"initializer"` Metadata ProjectMetadata `json:"metadata"` - UsedBy ProjectUsedBy `json:"usedBy"` - JSON projectJSON `json:"-"` + // technical_description is a detailed technical description of the project This + // field is not returned by default in GetProject or ListProjects responses + TechnicalDescription string `json:"technicalDescription"` + UsedBy ProjectUsedBy `json:"usedBy"` + JSON projectJSON `json:"-"` } // projectJSON contains the JSON metadata for the struct [Project] @@ -440,6 +443,7 @@ type projectJSON struct { DevcontainerFilePath apijson.Field Initializer apijson.Field Metadata apijson.Field + TechnicalDescription apijson.Field UsedBy apijson.Field raw string ExtraFields map[string]apijson.Field @@ -828,6 +832,9 @@ type ProjectNewParams struct { // ``` DevcontainerFilePath param.Field[string] `json:"devcontainerFilePath"` Name param.Field[string] `json:"name"` + // technical_description is a detailed technical description of the project This + // field is not returned by default in GetProject or ListProjects responses 8KB max + TechnicalDescription param.Field[string] `json:"technicalDescription"` } func (r ProjectNewParams) MarshalJSON() (data []byte, err error) { @@ -864,6 +871,9 @@ type ProjectUpdateParams struct { Name param.Field[string] `json:"name"` // project_id specifies the project identifier ProjectID param.Field[string] `json:"projectId" format:"uuid"` + // technical_description is a detailed technical description of the project This + // field is not returned by default in GetProject or ListProjects responses 8KB max + TechnicalDescription param.Field[string] `json:"technicalDescription"` } func (r ProjectUpdateParams) MarshalJSON() (data []byte, err error) { @@ -871,8 +881,9 @@ func (r ProjectUpdateParams) MarshalJSON() (data []byte, err error) { } type ProjectListParams struct { - Token param.Field[string] `query:"token"` - PageSize param.Field[int64] `query:"pageSize"` + Token param.Field[string] `query:"token"` + PageSize param.Field[int64] `query:"pageSize"` + Filter param.Field[ProjectListParamsFilter] `json:"filter"` // pagination contains the pagination options for listing organizations Pagination param.Field[ProjectListParamsPagination] `json:"pagination"` } @@ -889,6 +900,15 @@ func (r ProjectListParams) URLQuery() (v url.Values) { }) } +type ProjectListParamsFilter struct { + // project_ids filters the response to only projects with these IDs + ProjectIDs param.Field[[]string] `json:"projectIds" format:"uuid"` +} + +func (r ProjectListParamsFilter) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + // pagination contains the pagination options for listing organizations type ProjectListParamsPagination struct { // Token for the next set of results that was returned as next_token of a diff --git a/project_test.go b/project_test.go index 9440846..cc95ff2 100644 --- a/project_test.go +++ b/project_test.go @@ -48,6 +48,7 @@ func TestProjectNewWithOptionalParams(t *testing.T) { AutomationsFilePath: gitpod.F("automationsFilePath"), DevcontainerFilePath: gitpod.F("devcontainerFilePath"), Name: gitpod.F("Web Application"), + TechnicalDescription: gitpod.F("technicalDescription"), }) if err != nil { var apierr *gitpod.Error @@ -117,8 +118,9 @@ func TestProjectUpdateWithOptionalParams(t *testing.T) { }), }}), }), - Name: gitpod.F("x"), - ProjectID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), + Name: gitpod.F("x"), + ProjectID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), + TechnicalDescription: gitpod.F("technicalDescription"), }) if err != nil { var apierr *gitpod.Error @@ -145,6 +147,9 @@ func TestProjectListWithOptionalParams(t *testing.T) { _, err := client.Projects.List(context.TODO(), gitpod.ProjectListParams{ Token: gitpod.F("token"), PageSize: gitpod.F(int64(0)), + Filter: gitpod.F(gitpod.ProjectListParamsFilter{ + ProjectIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}), + }), Pagination: gitpod.F(gitpod.ProjectListParamsPagination{ Token: gitpod.F("token"), PageSize: gitpod.F(int64(20)), diff --git a/projectpolicy_test.go b/projectpolicy_test.go index bb805d6..3cd9486 100644 --- a/projectpolicy_test.go +++ b/projectpolicy_test.go @@ -29,7 +29,7 @@ func TestProjectPolicyNewWithOptionalParams(t *testing.T) { _, err := client.Projects.Policies.New(context.TODO(), gitpod.ProjectPolicyNewParams{ GroupID: gitpod.F("f53d2330-3795-4c5d-a1f3-453121af9c60"), ProjectID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), - Role: gitpod.F(gitpod.ProjectRoleUnspecified), + Role: gitpod.F(gitpod.ProjectRoleAdmin), }) if err != nil { var apierr *gitpod.Error @@ -56,7 +56,7 @@ func TestProjectPolicyUpdateWithOptionalParams(t *testing.T) { _, err := client.Projects.Policies.Update(context.TODO(), gitpod.ProjectPolicyUpdateParams{ GroupID: gitpod.F("f53d2330-3795-4c5d-a1f3-453121af9c60"), ProjectID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), - Role: gitpod.F(gitpod.ProjectRoleUnspecified), + Role: gitpod.F(gitpod.ProjectRoleEditor), }) if err != nil { var apierr *gitpod.Error diff --git a/runner.go b/runner.go index 6967628..b41a132 100644 --- a/runner.go +++ b/runner.go @@ -328,6 +328,94 @@ func (r *RunnerService) ParseContextURL(ctx context.Context, body RunnerParseCon return } +type GatewayInfo struct { + // Gateway represents a system gateway that provides access to services + Gateway shared.Gateway `json:"gateway"` + // latency is the round-trip time of the runner to the gateway in milliseconds. + Latency string `json:"latency" format:"regex"` + JSON gatewayInfoJSON `json:"-"` +} + +// gatewayInfoJSON contains the JSON metadata for the struct [GatewayInfo] +type gatewayInfoJSON struct { + Gateway apijson.Field + Latency apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *GatewayInfo) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r gatewayInfoJSON) RawJSON() string { + return r.raw +} + +type LogLevel string + +const ( + LogLevelUnspecified LogLevel = "LOG_LEVEL_UNSPECIFIED" + LogLevelDebug LogLevel = "LOG_LEVEL_DEBUG" + LogLevelInfo LogLevel = "LOG_LEVEL_INFO" + LogLevelWarn LogLevel = "LOG_LEVEL_WARN" + LogLevelError LogLevel = "LOG_LEVEL_ERROR" +) + +func (r LogLevel) IsKnown() bool { + switch r { + case LogLevelUnspecified, LogLevelDebug, LogLevelInfo, LogLevelWarn, LogLevelError: + return true + } + return false +} + +type MetricsConfiguration struct { + // enabled indicates whether the runner should collect metrics + Enabled bool `json:"enabled"` + // password is the password to use for the metrics collector + Password string `json:"password"` + // url is the URL of the metrics collector + URL string `json:"url"` + // username is the username to use for the metrics collector + Username string `json:"username"` + JSON metricsConfigurationJSON `json:"-"` +} + +// metricsConfigurationJSON contains the JSON metadata for the struct +// [MetricsConfiguration] +type metricsConfigurationJSON struct { + Enabled apijson.Field + Password apijson.Field + URL apijson.Field + Username apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *MetricsConfiguration) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r metricsConfigurationJSON) RawJSON() string { + return r.raw +} + +type MetricsConfigurationParam struct { + // enabled indicates whether the runner should collect metrics + Enabled param.Field[bool] `json:"enabled"` + // password is the password to use for the metrics collector + Password param.Field[string] `json:"password"` + // url is the URL of the metrics collector + URL param.Field[string] `json:"url"` + // username is the username to use for the metrics collector + Username param.Field[string] `json:"username"` +} + +func (r MetricsConfigurationParam) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + type Runner struct { // Time when the Runner was created. CreatedAt time.Time `json:"createdAt" format:"date-time"` @@ -378,11 +466,14 @@ const ( RunnerCapabilityUnspecified RunnerCapability = "RUNNER_CAPABILITY_UNSPECIFIED" RunnerCapabilityFetchLocalScmIntegrations RunnerCapability = "RUNNER_CAPABILITY_FETCH_LOCAL_SCM_INTEGRATIONS" RunnerCapabilitySecretContainerRegistry RunnerCapability = "RUNNER_CAPABILITY_SECRET_CONTAINER_REGISTRY" + RunnerCapabilityAgentExecution RunnerCapability = "RUNNER_CAPABILITY_AGENT_EXECUTION" + RunnerCapabilityAllowEnvTokenPopulation RunnerCapability = "RUNNER_CAPABILITY_ALLOW_ENV_TOKEN_POPULATION" + RunnerCapabilityDefaultDevContainerImage RunnerCapability = "RUNNER_CAPABILITY_DEFAULT_DEV_CONTAINER_IMAGE" ) func (r RunnerCapability) IsKnown() bool { switch r { - case RunnerCapabilityUnspecified, RunnerCapabilityFetchLocalScmIntegrations, RunnerCapabilitySecretContainerRegistry: + case RunnerCapabilityUnspecified, RunnerCapabilityFetchLocalScmIntegrations, RunnerCapabilitySecretContainerRegistry, RunnerCapabilityAgentExecution, RunnerCapabilityAllowEnvTokenPopulation, RunnerCapabilityDefaultDevContainerImage: return true } return false @@ -391,6 +482,14 @@ func (r RunnerCapability) IsKnown() bool { type RunnerConfiguration struct { // auto_update indicates whether the runner should automatically update itself. AutoUpdate bool `json:"autoUpdate"` + // devcontainer_image_cache_enabled controls whether the devcontainer build cache + // is enabled for this runner. Only takes effect on supported runners, currently + // only AWS EC2 runners. + DevcontainerImageCacheEnabled bool `json:"devcontainerImageCacheEnabled"` + // log_level is the log level for the runner + LogLevel LogLevel `json:"logLevel"` + // metrics contains configuration for the runner's metrics collection + Metrics MetricsConfiguration `json:"metrics"` // Region to deploy the runner in, if applicable. This is mainly used for remote // runners, and is only a hint. The runner may be deployed in a different region. // See the runner's status for the actual region. @@ -403,11 +502,14 @@ type RunnerConfiguration struct { // runnerConfigurationJSON contains the JSON metadata for the struct // [RunnerConfiguration] type runnerConfigurationJSON struct { - AutoUpdate apijson.Field - Region apijson.Field - ReleaseChannel apijson.Field - raw string - ExtraFields map[string]apijson.Field + AutoUpdate apijson.Field + DevcontainerImageCacheEnabled apijson.Field + LogLevel apijson.Field + Metrics apijson.Field + Region apijson.Field + ReleaseChannel apijson.Field + raw string + ExtraFields map[string]apijson.Field } func (r *RunnerConfiguration) UnmarshalJSON(data []byte) (err error) { @@ -421,6 +523,14 @@ func (r runnerConfigurationJSON) RawJSON() string { type RunnerConfigurationParam struct { // auto_update indicates whether the runner should automatically update itself. AutoUpdate param.Field[bool] `json:"autoUpdate"` + // devcontainer_image_cache_enabled controls whether the devcontainer build cache + // is enabled for this runner. Only takes effect on supported runners, currently + // only AWS EC2 runners. + DevcontainerImageCacheEnabled param.Field[bool] `json:"devcontainerImageCacheEnabled"` + // log_level is the log level for the runner + LogLevel param.Field[LogLevel] `json:"logLevel"` + // metrics contains configuration for the runner's metrics collection + Metrics param.Field[MetricsConfigurationParam] `json:"metrics"` // Region to deploy the runner in, if applicable. This is mainly used for remote // runners, and is only a hint. The runner may be deployed in a different region. // See the runner's status for the actual region. @@ -482,11 +592,12 @@ const ( RunnerProviderAwsEc2 RunnerProvider = "RUNNER_PROVIDER_AWS_EC2" RunnerProviderLinuxHost RunnerProvider = "RUNNER_PROVIDER_LINUX_HOST" RunnerProviderDesktopMac RunnerProvider = "RUNNER_PROVIDER_DESKTOP_MAC" + RunnerProviderManaged RunnerProvider = "RUNNER_PROVIDER_MANAGED" ) func (r RunnerProvider) IsKnown() bool { switch r { - case RunnerProviderUnspecified, RunnerProviderAwsEc2, RunnerProviderLinuxHost, RunnerProviderDesktopMac: + case RunnerProviderUnspecified, RunnerProviderAwsEc2, RunnerProviderLinuxHost, RunnerProviderDesktopMac, RunnerProviderManaged: return true } return false @@ -550,7 +661,9 @@ type RunnerStatus struct { AdditionalInfo []shared.FieldValue `json:"additionalInfo"` // capabilities is a list of capabilities the runner supports. Capabilities []RunnerCapability `json:"capabilities"` - LogURL string `json:"logUrl"` + // gateway_info is information about the gateway to which the runner is connected. + GatewayInfo GatewayInfo `json:"gatewayInfo"` + LogURL string `json:"logUrl"` // The runner's reported message which is shown to users. This message adds more // context to the runner's phase. Message string `json:"message"` @@ -559,7 +672,7 @@ type RunnerStatus struct { // region is the region the runner is running in, if applicable. Region string `json:"region"` SystemDetails string `json:"systemDetails"` - // Time when the status was last udpated. + // Time when the status was last updated. UpdatedAt time.Time `json:"updatedAt" format:"date-time"` Version string `json:"version"` JSON runnerStatusJSON `json:"-"` @@ -569,6 +682,7 @@ type RunnerStatus struct { type runnerStatusJSON struct { AdditionalInfo apijson.Field Capabilities apijson.Field + GatewayInfo apijson.Field LogURL apijson.Field Message apijson.Field Phase apijson.Field @@ -775,9 +889,11 @@ func (r runnerNewRunnerTokenResponseJSON) RawJSON() string { } type RunnerParseContextURLResponse struct { - Git RunnerParseContextURLResponseGit `json:"git"` - OriginalContextURL string `json:"originalContextUrl"` - JSON runnerParseContextURLResponseJSON `json:"-"` + Git RunnerParseContextURLResponseGit `json:"git"` + OriginalContextURL string `json:"originalContextUrl"` + // project_ids is a list of projects to which the context URL belongs to. + ProjectIDs []string `json:"projectIds"` + JSON runnerParseContextURLResponseJSON `json:"-"` } // runnerParseContextURLResponseJSON contains the JSON metadata for the struct @@ -785,6 +901,7 @@ type RunnerParseContextURLResponse struct { type runnerParseContextURLResponseJSON struct { Git apijson.Field OriginalContextURL apijson.Field + ProjectIDs apijson.Field raw string ExtraFields map[string]apijson.Field } @@ -840,8 +957,11 @@ type RunnerNewParams struct { // The specific implementation type of the runner This field is optional for // backwards compatibility but will be required in the future. When specified, kind // must not be specified (will be deduced from provider) - Provider param.Field[RunnerProvider] `json:"provider"` - Spec param.Field[RunnerSpecParam] `json:"spec"` + Provider param.Field[RunnerProvider] `json:"provider"` + // The runner manager id specifies the runner manager for the managed runner. This + // field is mandatory for managed runners, otheriwse should not be set. + RunnerManagerID param.Field[string] `json:"runnerManagerId" format:"uuid"` + Spec param.Field[RunnerSpecParam] `json:"spec"` } func (r RunnerNewParams) MarshalJSON() (data []byte, err error) { @@ -891,6 +1011,13 @@ func (r RunnerUpdateParamsSpec) MarshalJSON() (data []byte, err error) { type RunnerUpdateParamsSpecConfiguration struct { // auto_update indicates whether the runner should automatically update itself. AutoUpdate param.Field[bool] `json:"autoUpdate"` + // devcontainer_image_cache_enabled controls whether the shared devcontainer build + // cache is enabled for this runner. + DevcontainerImageCacheEnabled param.Field[bool] `json:"devcontainerImageCacheEnabled"` + // log_level is the log level for the runner + LogLevel param.Field[LogLevel] `json:"logLevel"` + // metrics contains configuration for the runner's metrics collection + Metrics param.Field[RunnerUpdateParamsSpecConfigurationMetrics] `json:"metrics"` // The release channel the runner is on ReleaseChannel param.Field[RunnerReleaseChannel] `json:"releaseChannel"` } @@ -899,6 +1026,22 @@ func (r RunnerUpdateParamsSpecConfiguration) MarshalJSON() (data []byte, err err return apijson.MarshalRoot(r) } +// metrics contains configuration for the runner's metrics collection +type RunnerUpdateParamsSpecConfigurationMetrics struct { + // enabled indicates whether the runner should collect metrics + Enabled param.Field[bool] `json:"enabled"` + // password is the password to use for the metrics collector + Password param.Field[string] `json:"password"` + // url is the URL of the metrics collector + URL param.Field[string] `json:"url"` + // username is the username to use for the metrics collector + Username param.Field[string] `json:"username"` +} + +func (r RunnerUpdateParamsSpecConfigurationMetrics) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + type RunnerListParams struct { Token param.Field[string] `query:"token"` PageSize param.Field[int64] `query:"pageSize"` diff --git a/runner_test.go b/runner_test.go index 14edad8..ac8f9ca 100644 --- a/runner_test.go +++ b/runner_test.go @@ -27,16 +27,25 @@ func TestRunnerNewWithOptionalParams(t *testing.T) { option.WithBearerToken("My Bearer Token"), ) _, err := client.Runners.New(context.TODO(), gitpod.RunnerNewParams{ - Kind: gitpod.F(gitpod.RunnerKindUnspecified), - Name: gitpod.F("Production Runner"), - Provider: gitpod.F(gitpod.RunnerProviderUnspecified), + Kind: gitpod.F(gitpod.RunnerKindUnspecified), + Name: gitpod.F("Production Runner"), + Provider: gitpod.F(gitpod.RunnerProviderAwsEc2), + RunnerManagerID: gitpod.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), Spec: gitpod.F(gitpod.RunnerSpecParam{ Configuration: gitpod.F(gitpod.RunnerConfigurationParam{ - AutoUpdate: gitpod.F(true), + AutoUpdate: gitpod.F(true), + DevcontainerImageCacheEnabled: gitpod.F(true), + LogLevel: gitpod.F(gitpod.LogLevelUnspecified), + Metrics: gitpod.F(gitpod.MetricsConfigurationParam{ + Enabled: gitpod.F(true), + Password: gitpod.F("password"), + URL: gitpod.F("url"), + Username: gitpod.F("username"), + }), Region: gitpod.F("us-west"), - ReleaseChannel: gitpod.F(gitpod.RunnerReleaseChannelUnspecified), + ReleaseChannel: gitpod.F(gitpod.RunnerReleaseChannelStable), }), - DesiredPhase: gitpod.F(gitpod.RunnerPhaseUnspecified), + DesiredPhase: gitpod.F(gitpod.RunnerPhaseActive), }), }) if err != nil { @@ -91,8 +100,16 @@ func TestRunnerUpdateWithOptionalParams(t *testing.T) { RunnerID: gitpod.F("d2c94c27-3b76-4a42-b88c-95a85e392c68"), Spec: gitpod.F(gitpod.RunnerUpdateParamsSpec{ Configuration: gitpod.F(gitpod.RunnerUpdateParamsSpecConfiguration{ - AutoUpdate: gitpod.F(true), - ReleaseChannel: gitpod.F(gitpod.RunnerReleaseChannelUnspecified), + AutoUpdate: gitpod.F(true), + DevcontainerImageCacheEnabled: gitpod.F(true), + LogLevel: gitpod.F(gitpod.LogLevelUnspecified), + Metrics: gitpod.F(gitpod.RunnerUpdateParamsSpecConfigurationMetrics{ + Enabled: gitpod.F(true), + Password: gitpod.F("password"), + URL: gitpod.F("url"), + Username: gitpod.F("username"), + }), + ReleaseChannel: gitpod.F(gitpod.RunnerReleaseChannelLatest), }), DesiredPhase: gitpod.F(gitpod.RunnerPhaseUnspecified), }), @@ -125,7 +142,7 @@ func TestRunnerListWithOptionalParams(t *testing.T) { Filter: gitpod.F(gitpod.RunnerListParamsFilter{ CreatorIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}), Kinds: gitpod.F([]gitpod.RunnerKind{gitpod.RunnerKindUnspecified}), - Providers: gitpod.F([]gitpod.RunnerProvider{gitpod.RunnerProviderUnspecified}), + Providers: gitpod.F([]gitpod.RunnerProvider{gitpod.RunnerProviderAwsEc2}), }), Pagination: gitpod.F(gitpod.RunnerListParamsPagination{ Token: gitpod.F("token"), diff --git a/runnerconfiguration.go b/runnerconfiguration.go index 46a83e4..f4b8c73 100644 --- a/runnerconfiguration.go +++ b/runnerconfiguration.go @@ -186,6 +186,9 @@ type RunnerConfigurationValidateParamsScmIntegration struct { // id is the unique identifier of the SCM integration ID param.Field[string] `json:"id"` Host param.Field[string] `json:"host"` + // issuer_url can be set to override the authentication provider URL, if it doesn't + // match the SCM host. + IssuerURL param.Field[string] `json:"issuerUrl"` // oauth_client_id is the OAuth app's client ID, if OAuth is configured. If // configured, oauth_client_secret must also be set. OAuthClientID param.Field[string] `json:"oauthClientId"` diff --git a/runnerconfiguration_test.go b/runnerconfiguration_test.go index 36eb1f5..11859a2 100644 --- a/runnerconfiguration_test.go +++ b/runnerconfiguration_test.go @@ -43,6 +43,7 @@ func TestRunnerConfigurationValidateWithOptionalParams(t *testing.T) { ScmIntegration: gitpod.F(gitpod.RunnerConfigurationValidateParamsScmIntegration{ ID: gitpod.F("integration-id"), Host: gitpod.F("github.com"), + IssuerURL: gitpod.F("issuerUrl"), OAuthClientID: gitpod.F("client_id"), OAuthEncryptedClientSecret: gitpod.F("U3RhaW5sZXNzIHJvY2tz"), OAuthPlaintextClientSecret: gitpod.F("client_secret"), diff --git a/runnerconfigurationhostauthenticationtoken_test.go b/runnerconfigurationhostauthenticationtoken_test.go index e553890..bfc7bb8 100644 --- a/runnerconfigurationhostauthenticationtoken_test.go +++ b/runnerconfigurationhostauthenticationtoken_test.go @@ -33,7 +33,7 @@ func TestRunnerConfigurationHostAuthenticationTokenNewWithOptionalParams(t *test Host: gitpod.F("github.com"), RefreshToken: gitpod.F("ghr_xxxxxxxxxxxx"), RunnerID: gitpod.F("d2c94c27-3b76-4a42-b88c-95a85e392c68"), - Source: gitpod.F(gitpod.HostAuthenticationTokenSourceUnspecified), + Source: gitpod.F(gitpod.HostAuthenticationTokenSourceOAuth), UserID: gitpod.F("f53d2330-3795-4c5d-a1f3-453121af9c60"), }) if err != nil { diff --git a/runnerconfigurationschema.go b/runnerconfigurationschema.go index 22f1ea1..8ad2f47 100644 --- a/runnerconfigurationschema.go +++ b/runnerconfigurationschema.go @@ -165,18 +165,28 @@ func (r runnerConfigurationSchemaEnvironmentClassesDisplayJSON) RawJSON() string } type RunnerConfigurationSchemaEnvironmentClassesEnum struct { - Default string `json:"default"` - Values []string `json:"values"` - JSON runnerConfigurationSchemaEnvironmentClassesEnumJSON `json:"-"` + // deprecated, will be removed, use default_value instead + // + // Deprecated: deprecated + Default string `json:"default"` + DefaultValue RunnerConfigurationSchemaEnvironmentClassesEnumDefaultValue `json:"defaultValue"` + PossibleValues []RunnerConfigurationSchemaEnvironmentClassesEnumPossibleValue `json:"possibleValues"` + // deprecated, will be removed, use possible_values instead + // + // Deprecated: deprecated + Values []string `json:"values"` + JSON runnerConfigurationSchemaEnvironmentClassesEnumJSON `json:"-"` } // runnerConfigurationSchemaEnvironmentClassesEnumJSON contains the JSON metadata // for the struct [RunnerConfigurationSchemaEnvironmentClassesEnum] type runnerConfigurationSchemaEnvironmentClassesEnumJSON struct { - Default apijson.Field - Values apijson.Field - raw string - ExtraFields map[string]apijson.Field + Default apijson.Field + DefaultValue apijson.Field + PossibleValues apijson.Field + Values apijson.Field + raw string + ExtraFields map[string]apijson.Field } func (r *RunnerConfigurationSchemaEnvironmentClassesEnum) UnmarshalJSON(data []byte) (err error) { @@ -187,6 +197,58 @@ func (r runnerConfigurationSchemaEnvironmentClassesEnumJSON) RawJSON() string { return r.raw } +type RunnerConfigurationSchemaEnvironmentClassesEnumDefaultValue struct { + Detail string `json:"detail"` + Subtitle string `json:"subtitle"` + Title string `json:"title"` + JSON runnerConfigurationSchemaEnvironmentClassesEnumDefaultValueJSON `json:"-"` +} + +// runnerConfigurationSchemaEnvironmentClassesEnumDefaultValueJSON contains the +// JSON metadata for the struct +// [RunnerConfigurationSchemaEnvironmentClassesEnumDefaultValue] +type runnerConfigurationSchemaEnvironmentClassesEnumDefaultValueJSON struct { + Detail apijson.Field + Subtitle apijson.Field + Title apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *RunnerConfigurationSchemaEnvironmentClassesEnumDefaultValue) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r runnerConfigurationSchemaEnvironmentClassesEnumDefaultValueJSON) RawJSON() string { + return r.raw +} + +type RunnerConfigurationSchemaEnvironmentClassesEnumPossibleValue struct { + Detail string `json:"detail"` + Subtitle string `json:"subtitle"` + Title string `json:"title"` + JSON runnerConfigurationSchemaEnvironmentClassesEnumPossibleValueJSON `json:"-"` +} + +// runnerConfigurationSchemaEnvironmentClassesEnumPossibleValueJSON contains the +// JSON metadata for the struct +// [RunnerConfigurationSchemaEnvironmentClassesEnumPossibleValue] +type runnerConfigurationSchemaEnvironmentClassesEnumPossibleValueJSON struct { + Detail apijson.Field + Subtitle apijson.Field + Title apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *RunnerConfigurationSchemaEnvironmentClassesEnumPossibleValue) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r runnerConfigurationSchemaEnvironmentClassesEnumPossibleValueJSON) RawJSON() string { + return r.raw +} + type RunnerConfigurationSchemaEnvironmentClassesInt struct { Default int64 `json:"default"` Max int64 `json:"max"` @@ -317,18 +379,28 @@ func (r runnerConfigurationSchemaRunnerConfigDisplayJSON) RawJSON() string { } type RunnerConfigurationSchemaRunnerConfigEnum struct { - Default string `json:"default"` - Values []string `json:"values"` - JSON runnerConfigurationSchemaRunnerConfigEnumJSON `json:"-"` + // deprecated, will be removed, use default_value instead + // + // Deprecated: deprecated + Default string `json:"default"` + DefaultValue RunnerConfigurationSchemaRunnerConfigEnumDefaultValue `json:"defaultValue"` + PossibleValues []RunnerConfigurationSchemaRunnerConfigEnumPossibleValue `json:"possibleValues"` + // deprecated, will be removed, use possible_values instead + // + // Deprecated: deprecated + Values []string `json:"values"` + JSON runnerConfigurationSchemaRunnerConfigEnumJSON `json:"-"` } // runnerConfigurationSchemaRunnerConfigEnumJSON contains the JSON metadata for the // struct [RunnerConfigurationSchemaRunnerConfigEnum] type runnerConfigurationSchemaRunnerConfigEnumJSON struct { - Default apijson.Field - Values apijson.Field - raw string - ExtraFields map[string]apijson.Field + Default apijson.Field + DefaultValue apijson.Field + PossibleValues apijson.Field + Values apijson.Field + raw string + ExtraFields map[string]apijson.Field } func (r *RunnerConfigurationSchemaRunnerConfigEnum) UnmarshalJSON(data []byte) (err error) { @@ -339,6 +411,56 @@ func (r runnerConfigurationSchemaRunnerConfigEnumJSON) RawJSON() string { return r.raw } +type RunnerConfigurationSchemaRunnerConfigEnumDefaultValue struct { + Detail string `json:"detail"` + Subtitle string `json:"subtitle"` + Title string `json:"title"` + JSON runnerConfigurationSchemaRunnerConfigEnumDefaultValueJSON `json:"-"` +} + +// runnerConfigurationSchemaRunnerConfigEnumDefaultValueJSON contains the JSON +// metadata for the struct [RunnerConfigurationSchemaRunnerConfigEnumDefaultValue] +type runnerConfigurationSchemaRunnerConfigEnumDefaultValueJSON struct { + Detail apijson.Field + Subtitle apijson.Field + Title apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *RunnerConfigurationSchemaRunnerConfigEnumDefaultValue) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r runnerConfigurationSchemaRunnerConfigEnumDefaultValueJSON) RawJSON() string { + return r.raw +} + +type RunnerConfigurationSchemaRunnerConfigEnumPossibleValue struct { + Detail string `json:"detail"` + Subtitle string `json:"subtitle"` + Title string `json:"title"` + JSON runnerConfigurationSchemaRunnerConfigEnumPossibleValueJSON `json:"-"` +} + +// runnerConfigurationSchemaRunnerConfigEnumPossibleValueJSON contains the JSON +// metadata for the struct [RunnerConfigurationSchemaRunnerConfigEnumPossibleValue] +type runnerConfigurationSchemaRunnerConfigEnumPossibleValueJSON struct { + Detail apijson.Field + Subtitle apijson.Field + Title apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *RunnerConfigurationSchemaRunnerConfigEnumPossibleValue) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r runnerConfigurationSchemaRunnerConfigEnumPossibleValueJSON) RawJSON() string { + return r.raw +} + type RunnerConfigurationSchemaRunnerConfigInt struct { Default int64 `json:"default"` Max int64 `json:"max"` diff --git a/runnerconfigurationscmintegration.go b/runnerconfigurationscmintegration.go index 9617bee..8616ae1 100644 --- a/runnerconfigurationscmintegration.go +++ b/runnerconfigurationscmintegration.go @@ -235,8 +235,13 @@ type ScmIntegrationOAuthConfig struct { ClientID string `json:"clientId"` // encrypted_client_secret is the OAuth app's secret encrypted with the runner's // public key. - EncryptedClientSecret string `json:"encryptedClientSecret" format:"byte"` - JSON scmIntegrationOAuthConfigJSON `json:"-"` + EncryptedClientSecret string `json:"encryptedClientSecret" format:"byte"` + // issuer_url is used to override the authentication provider URL, if it doesn't + // match the SCM host. + // + // +optional if not set, this account is owned by the installation. + IssuerURL string `json:"issuerUrl"` + JSON scmIntegrationOAuthConfigJSON `json:"-"` } // scmIntegrationOAuthConfigJSON contains the JSON metadata for the struct @@ -244,6 +249,7 @@ type ScmIntegrationOAuthConfig struct { type scmIntegrationOAuthConfigJSON struct { ClientID apijson.Field EncryptedClientSecret apijson.Field + IssuerURL apijson.Field raw string ExtraFields map[string]apijson.Field } @@ -305,6 +311,9 @@ type RunnerConfigurationScmIntegrationDeleteResponse = interface{} type RunnerConfigurationScmIntegrationNewParams struct { Host param.Field[string] `json:"host"` + // issuer_url can be set to override the authentication provider URL, if it doesn't + // match the SCM host. + IssuerURL param.Field[string] `json:"issuerUrl"` // oauth_client_id is the OAuth app's client ID, if OAuth is configured. If // configured, oauth_plaintext_client_secret must also be set. OAuthClientID param.Field[string] `json:"oauthClientId"` @@ -332,6 +341,9 @@ func (r RunnerConfigurationScmIntegrationGetParams) MarshalJSON() (data []byte, type RunnerConfigurationScmIntegrationUpdateParams struct { ID param.Field[string] `json:"id" format:"uuid"` + // issuer_url can be set to override the authentication provider URL, if it doesn't + // match the SCM host. + IssuerURL param.Field[string] `json:"issuerUrl"` // oauth_client_id can be set to update the OAuth app's client ID. If an empty // string is set, the OAuth configuration will be removed (regardless of whether a // client secret is set), and any existing Host Authentication Tokens for the SCM diff --git a/runnerconfigurationscmintegration_test.go b/runnerconfigurationscmintegration_test.go index 8753bdf..1f34966 100644 --- a/runnerconfigurationscmintegration_test.go +++ b/runnerconfigurationscmintegration_test.go @@ -28,6 +28,7 @@ func TestRunnerConfigurationScmIntegrationNewWithOptionalParams(t *testing.T) { ) _, err := client.Runners.Configurations.ScmIntegrations.New(context.TODO(), gitpod.RunnerConfigurationScmIntegrationNewParams{ Host: gitpod.F("github.com"), + IssuerURL: gitpod.F("issuerUrl"), OAuthClientID: gitpod.F("client_id"), OAuthPlaintextClientSecret: gitpod.F("client_secret"), Pat: gitpod.F(true), @@ -83,6 +84,7 @@ func TestRunnerConfigurationScmIntegrationUpdateWithOptionalParams(t *testing.T) ) _, err := client.Runners.Configurations.ScmIntegrations.Update(context.TODO(), gitpod.RunnerConfigurationScmIntegrationUpdateParams{ ID: gitpod.F("d2c94c27-3b76-4a42-b88c-95a85e392c68"), + IssuerURL: gitpod.F("issuerUrl"), OAuthClientID: gitpod.F("new_client_id"), OAuthPlaintextClientSecret: gitpod.F("new_client_secret"), Pat: gitpod.F(true), diff --git a/runnerpolicy_test.go b/runnerpolicy_test.go index 9b93124..cc03a29 100644 --- a/runnerpolicy_test.go +++ b/runnerpolicy_test.go @@ -28,7 +28,7 @@ func TestRunnerPolicyNewWithOptionalParams(t *testing.T) { ) _, err := client.Runners.Policies.New(context.TODO(), gitpod.RunnerPolicyNewParams{ GroupID: gitpod.F("f53d2330-3795-4c5d-a1f3-453121af9c60"), - Role: gitpod.F(gitpod.RunnerRoleUnspecified), + Role: gitpod.F(gitpod.RunnerRoleAdmin), RunnerID: gitpod.F("d2c94c27-3b76-4a42-b88c-95a85e392c68"), }) if err != nil { @@ -55,7 +55,7 @@ func TestRunnerPolicyUpdateWithOptionalParams(t *testing.T) { ) _, err := client.Runners.Policies.Update(context.TODO(), gitpod.RunnerPolicyUpdateParams{ GroupID: gitpod.F("f53d2330-3795-4c5d-a1f3-453121af9c60"), - Role: gitpod.F(gitpod.RunnerRoleUnspecified), + Role: gitpod.F(gitpod.RunnerRoleUser), RunnerID: gitpod.F("d2c94c27-3b76-4a42-b88c-95a85e392c68"), }) if err != nil { diff --git a/scripts/bootstrap b/scripts/bootstrap index ed03e52..d6ac165 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ]; then brew bundle check >/dev/null 2>&1 || { echo "==> Installing Homebrew dependencies…" brew bundle @@ -13,4 +13,4 @@ fi echo "==> Installing Go dependencies…" -go mod tidy +go mod tidy -e diff --git a/secret.go b/secret.go index d9ff134..bc16e92 100644 --- a/secret.go +++ b/secret.go @@ -86,12 +86,12 @@ func (r *SecretService) New(ctx context.Context, body SecretNewParams, opts ...o return } -// Lists secrets with optional filtering. +// Lists secrets // // Use this method to: // // - View all project secrets -// - Filter secrets by project +// - View all user secrets // // ### Examples // @@ -101,7 +101,20 @@ func (r *SecretService) New(ctx context.Context, body SecretNewParams, opts ...o // // ```yaml // filter: -// projectIds: ["b0e12f6c-4c67-429d-a4a6-d9838b5da047"] +// scope: +// projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047" +// pagination: +// pageSize: 20 +// ``` +// +// - List user secrets: +// +// Shows all secrets for a user. +// +// ```yaml +// filter: +// scope: +// userId: "123e4567-e89b-12d3-a456-426614174000" // pagination: // pageSize: 20 // ``` @@ -122,12 +135,12 @@ func (r *SecretService) List(ctx context.Context, params SecretListParams, opts return res, nil } -// Lists secrets with optional filtering. +// Lists secrets // // Use this method to: // // - View all project secrets -// - Filter secrets by project +// - View all user secrets // // ### Examples // @@ -137,7 +150,20 @@ func (r *SecretService) List(ctx context.Context, params SecretListParams, opts // // ```yaml // filter: -// projectIds: ["b0e12f6c-4c67-429d-a4a6-d9838b5da047"] +// scope: +// projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047" +// pagination: +// pageSize: 20 +// ``` +// +// - List user secrets: +// +// Shows all secrets for a user. +// +// ```yaml +// filter: +// scope: +// userId: "123e4567-e89b-12d3-a456-426614174000" // pagination: // pageSize: 20 // ``` @@ -318,8 +344,11 @@ type Secret struct { FilePath string `json:"filePath"` // Name of the secret for humans. Name string `json:"name"` - // The Project ID this Secret belongs to - ProjectID string `json:"projectId" format:"uuid"` + // The Project ID this Secret belongs to Deprecated: use scope instead + // + // Deprecated: deprecated + ProjectID string `json:"projectId" format:"uuid"` + Scope SecretScope `json:"scope"` // A Timestamp represents a point in time independent of any time zone or local // calendar, encoded as a count of seconds and fractions of seconds at nanosecond // resolution. The count is relative to an epoch at UTC midnight on January 1, @@ -422,6 +451,7 @@ type secretJSON struct { FilePath apijson.Field Name apijson.Field ProjectID apijson.Field + Scope apijson.Field UpdatedAt apijson.Field raw string ExtraFields map[string]apijson.Field @@ -435,6 +465,41 @@ func (r secretJSON) RawJSON() string { return r.raw } +type SecretScope struct { + // project_id is the Project ID this Secret belongs to + ProjectID string `json:"projectId" format:"uuid"` + // user_id is the User ID this Secret belongs to + UserID string `json:"userId" format:"uuid"` + JSON secretScopeJSON `json:"-"` +} + +// secretScopeJSON contains the JSON metadata for the struct [SecretScope] +type secretScopeJSON struct { + ProjectID apijson.Field + UserID apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *SecretScope) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r secretScopeJSON) RawJSON() string { + return r.raw +} + +type SecretScopeParam struct { + // project_id is the Project ID this Secret belongs to + ProjectID param.Field[string] `json:"projectId" format:"uuid"` + // user_id is the User ID this Secret belongs to + UserID param.Field[string] `json:"userId" format:"uuid"` +} + +func (r SecretScopeParam) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + type SecretNewResponse struct { Secret Secret `json:"secret"` JSON secretNewResponseJSON `json:"-"` @@ -483,12 +548,7 @@ type SecretUpdateValueResponse = interface{} type SecretNewParams struct { // secret will be mounted as a docker config in the environment VM, mount will have - // the docker registry host value must be a valid registry host (e.g. - // registry.docker.com, https://registry.docker.com, ghcr.io:5050): - // - // ``` - // this.matches('^[a-zA-Z0-9.-/:]+(:[0-9]+)?$') - // ``` + // the docker registry host ContainerRegistryBasicAuthHost param.Field[string] `json:"containerRegistryBasicAuthHost"` // secret will be created as an Environment Variable with the same name as the // secret @@ -501,8 +561,10 @@ type SecretNewParams struct { // ``` FilePath param.Field[string] `json:"filePath"` Name param.Field[string] `json:"name"` - // project_id is the ProjectID this Secret belongs to - ProjectID param.Field[string] `json:"projectId" format:"uuid"` + // project_id is the ProjectID this Secret belongs to Deprecated: use scope instead + ProjectID param.Field[string] `json:"projectId"` + // scope is the scope of the secret + Scope param.Field[SecretScopeParam] `json:"scope"` // value is the plaintext value of the secret Value param.Field[string] `json:"value"` } @@ -533,7 +595,12 @@ func (r SecretListParams) URLQuery() (v url.Values) { type SecretListParamsFilter struct { // project_ids filters the response to only Secrets used by these Project IDs + // Deprecated: use scope instead. Values in project_ids will be ignored. + // + // Deprecated: deprecated ProjectIDs param.Field[[]string] `json:"projectIds" format:"uuid"` + // scope is the scope of the secrets to list + Scope param.Field[SecretScopeParam] `json:"scope"` } func (r SecretListParamsFilter) MarshalJSON() (data []byte, err error) { diff --git a/secret_test.go b/secret_test.go index c6bf30b..6255feb 100644 --- a/secret_test.go +++ b/secret_test.go @@ -32,7 +32,11 @@ func TestSecretNewWithOptionalParams(t *testing.T) { FilePath: gitpod.F("filePath"), Name: gitpod.F("DATABASE_URL"), ProjectID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), - Value: gitpod.F("postgresql://user:pass@localhost:5432/db"), + Scope: gitpod.F(gitpod.SecretScopeParam{ + ProjectID: gitpod.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), + UserID: gitpod.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), + }), + Value: gitpod.F("postgresql://user:pass@localhost:5432/db"), }) if err != nil { var apierr *gitpod.Error @@ -60,7 +64,11 @@ func TestSecretListWithOptionalParams(t *testing.T) { Token: gitpod.F("token"), PageSize: gitpod.F(int64(0)), Filter: gitpod.F(gitpod.SecretListParamsFilter{ - ProjectIDs: gitpod.F([]string{"b0e12f6c-4c67-429d-a4a6-d9838b5da047"}), + ProjectIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}), + Scope: gitpod.F(gitpod.SecretScopeParam{ + ProjectID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"), + UserID: gitpod.F("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"), + }), }), Pagination: gitpod.F(gitpod.SecretListParamsPagination{ Token: gitpod.F("token"), diff --git a/shared/shared.go b/shared/shared.go index d44e3cc..bec0078 100644 --- a/shared/shared.go +++ b/shared/shared.go @@ -143,6 +143,35 @@ func (r FieldValueParam) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } +// Gateway represents a system gateway that provides access to services +type Gateway struct { + // name is the human-readable name of the gateway. name is unique across all + // gateways. + Name string `json:"name,required"` + // url of the gateway + URL string `json:"url,required"` + // region is the geographical region where the gateway is located + Region string `json:"region"` + JSON gatewayJSON `json:"-"` +} + +// gatewayJSON contains the JSON metadata for the struct [Gateway] +type gatewayJSON struct { + Name apijson.Field + URL apijson.Field + Region apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *Gateway) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r gatewayJSON) RawJSON() string { + return r.raw +} + type OrganizationRole string const ( @@ -168,11 +197,12 @@ const ( PrincipalRunner Principal = "PRINCIPAL_RUNNER" PrincipalEnvironment Principal = "PRINCIPAL_ENVIRONMENT" PrincipalServiceAccount Principal = "PRINCIPAL_SERVICE_ACCOUNT" + PrincipalRunnerManager Principal = "PRINCIPAL_RUNNER_MANAGER" ) func (r Principal) IsKnown() bool { switch r { - case PrincipalUnspecified, PrincipalAccount, PrincipalUser, PrincipalRunner, PrincipalEnvironment, PrincipalServiceAccount: + case PrincipalUnspecified, PrincipalAccount, PrincipalUser, PrincipalRunner, PrincipalEnvironment, PrincipalServiceAccount, PrincipalRunnerManager: return true } return false @@ -536,6 +566,9 @@ type TaskExecutionStatusStep struct { // failure_message summarises why the step failed to operate. If this is non-empty // the step has failed to operate and will likely transition to a failed state. FailureMessage string `json:"failureMessage"` + // output contains the output of the task execution. setting an output field to + // empty string will unset it. + Output map[string]string `json:"output"` // phase is the current phase of the execution step Phase TaskExecutionPhase `json:"phase"` JSON taskExecutionStatusStepJSON `json:"-"` @@ -546,6 +579,7 @@ type TaskExecutionStatusStep struct { type taskExecutionStatusStepJSON struct { ID apijson.Field FailureMessage apijson.Field + Output apijson.Field Phase apijson.Field raw string ExtraFields map[string]apijson.Field diff --git a/usage.go b/usage.go new file mode 100644 index 0000000..6f49fe1 --- /dev/null +++ b/usage.go @@ -0,0 +1,216 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package gitpod + +import ( + "context" + "net/http" + "net/url" + "time" + + "github.com/gitpod-io/gitpod-sdk-go/internal/apijson" + "github.com/gitpod-io/gitpod-sdk-go/internal/apiquery" + "github.com/gitpod-io/gitpod-sdk-go/internal/param" + "github.com/gitpod-io/gitpod-sdk-go/internal/requestconfig" + "github.com/gitpod-io/gitpod-sdk-go/option" + "github.com/gitpod-io/gitpod-sdk-go/packages/pagination" +) + +// UsageService contains methods and other services that help with interacting with +// the gitpod 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 [NewUsageService] method instead. +type UsageService struct { + Options []option.RequestOption +} + +// NewUsageService 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 NewUsageService(opts ...option.RequestOption) (r *UsageService) { + r = &UsageService{} + r.Options = opts + return +} + +// Lists completed environment runtime records within a specified date range. +// +// Returns a list of environment runtime records that were completed within the +// specified date range. Records of currently running environments are not +// included. +// +// Use this method to: +// +// - View environment runtime records +// - Filter by project +// - Create custom usage reports +// +// ### Example +// +// ```yaml +// filter: +// +// projectId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" +// dateRange: +// startTime: "2024-01-01T00:00:00Z" +// endTime: "2024-01-02T00:00:00Z" +// +// pagination: +// +// pageSize: 100 +// +// ``` +func (r *UsageService) ListEnvironmentRuntimeRecords(ctx context.Context, params UsageListEnvironmentRuntimeRecordsParams, opts ...option.RequestOption) (res *pagination.RecordsPage[EnvironmentUsageRecord], err error) { + var raw *http.Response + opts = append(r.Options[:], opts...) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) + path := "gitpod.v1.UsageService/ListEnvironmentUsageRecords" + cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodPost, path, params, &res, opts...) + if err != nil { + return nil, err + } + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +// Lists completed environment runtime records within a specified date range. +// +// Returns a list of environment runtime records that were completed within the +// specified date range. Records of currently running environments are not +// included. +// +// Use this method to: +// +// - View environment runtime records +// - Filter by project +// - Create custom usage reports +// +// ### Example +// +// ```yaml +// filter: +// +// projectId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" +// dateRange: +// startTime: "2024-01-01T00:00:00Z" +// endTime: "2024-01-02T00:00:00Z" +// +// pagination: +// +// pageSize: 100 +// +// ``` +func (r *UsageService) ListEnvironmentRuntimeRecordsAutoPaging(ctx context.Context, params UsageListEnvironmentRuntimeRecordsParams, opts ...option.RequestOption) *pagination.RecordsPageAutoPager[EnvironmentUsageRecord] { + return pagination.NewRecordsPageAutoPager(r.ListEnvironmentRuntimeRecords(ctx, params, opts...)) +} + +// EnvironmentUsageRecord represents a record of an environment from start to stop. +type EnvironmentUsageRecord struct { + // Environment usage record ID. + ID string `json:"id"` + // Time when the environment was created. + CreatedAt time.Time `json:"createdAt" format:"date-time"` + // Environment class ID associated with the record. + EnvironmentClassID string `json:"environmentClassId"` + // Environment ID associated with the record. + EnvironmentID string `json:"environmentId"` + // Project ID associated with the environment (if available). + ProjectID string `json:"projectId"` + // Runner ID associated with the environment. + RunnerID string `json:"runnerId"` + // Time when the environment was stopped. + StoppedAt time.Time `json:"stoppedAt" format:"date-time"` + // User ID is the ID of the user who created the environment associated with the + // record. + UserID string `json:"userId"` + JSON environmentUsageRecordJSON `json:"-"` +} + +// environmentUsageRecordJSON contains the JSON metadata for the struct +// [EnvironmentUsageRecord] +type environmentUsageRecordJSON struct { + ID apijson.Field + CreatedAt apijson.Field + EnvironmentClassID apijson.Field + EnvironmentID apijson.Field + ProjectID apijson.Field + RunnerID apijson.Field + StoppedAt apijson.Field + UserID apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *EnvironmentUsageRecord) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r environmentUsageRecordJSON) RawJSON() string { + return r.raw +} + +type UsageListEnvironmentRuntimeRecordsParams struct { + Token param.Field[string] `query:"token"` + PageSize param.Field[int64] `query:"pageSize"` + // Filter options. + Filter param.Field[UsageListEnvironmentRuntimeRecordsParamsFilter] `json:"filter"` + // Pagination options. + Pagination param.Field[UsageListEnvironmentRuntimeRecordsParamsPagination] `json:"pagination"` +} + +func (r UsageListEnvironmentRuntimeRecordsParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +// URLQuery serializes [UsageListEnvironmentRuntimeRecordsParams]'s query +// parameters as `url.Values`. +func (r UsageListEnvironmentRuntimeRecordsParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatComma, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +// Filter options. +type UsageListEnvironmentRuntimeRecordsParamsFilter struct { + // Date range to query runtime records within. + DateRange param.Field[UsageListEnvironmentRuntimeRecordsParamsFilterDateRange] `json:"dateRange,required"` + // Optional project ID to filter runtime records by. + ProjectID param.Field[string] `json:"projectId"` +} + +func (r UsageListEnvironmentRuntimeRecordsParamsFilter) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +// Date range to query runtime records within. +type UsageListEnvironmentRuntimeRecordsParamsFilterDateRange struct { + // End time of the date range (exclusive). + EndTime param.Field[time.Time] `json:"endTime,required" format:"date-time"` + // Start time of the date range (inclusive). + StartTime param.Field[time.Time] `json:"startTime,required" format:"date-time"` +} + +func (r UsageListEnvironmentRuntimeRecordsParamsFilterDateRange) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +// Pagination options. +type UsageListEnvironmentRuntimeRecordsParamsPagination struct { + // Token for the next set of results that was returned as next_token of a + // PaginationResponse + Token param.Field[string] `json:"token"` + // Page size is the maximum number of results to retrieve per page. Defaults to 25. + // Maximum 100. + PageSize param.Field[int64] `json:"pageSize"` +} + +func (r UsageListEnvironmentRuntimeRecordsParamsPagination) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} diff --git a/usage1_test.go b/usage1_test.go new file mode 100644 index 0000000..6d6b8f3 --- /dev/null +++ b/usage1_test.go @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package gitpod_test + +import ( + "context" + "os" + "testing" + + "github.com/gitpod-io/gitpod-sdk-go" + "github.com/gitpod-io/gitpod-sdk-go/internal/testutil" + "github.com/gitpod-io/gitpod-sdk-go/option" +) + +func TestUsage(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + response, err := client.Identity.GetAuthenticatedIdentity(context.TODO(), gitpod.IdentityGetAuthenticatedIdentityParams{}) + if err != nil { + t.Error(err) + return + } + t.Logf("%+v\n", response.OrganizationID) +} diff --git a/usage_test.go b/usage_test.go index c1454a1..0ec9957 100644 --- a/usage_test.go +++ b/usage_test.go @@ -4,15 +4,18 @@ package gitpod_test import ( "context" + "errors" "os" "testing" + "time" "github.com/gitpod-io/gitpod-sdk-go" "github.com/gitpod-io/gitpod-sdk-go/internal/testutil" "github.com/gitpod-io/gitpod-sdk-go/option" ) -func TestUsage(t *testing.T) { +func TestUsageListEnvironmentRuntimeRecordsWithOptionalParams(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 @@ -24,9 +27,26 @@ func TestUsage(t *testing.T) { option.WithBaseURL(baseURL), option.WithBearerToken("My Bearer Token"), ) - response, err := client.Identity.GetAuthenticatedIdentity(context.TODO(), gitpod.IdentityGetAuthenticatedIdentityParams{}) + _, err := client.Usage.ListEnvironmentRuntimeRecords(context.TODO(), gitpod.UsageListEnvironmentRuntimeRecordsParams{ + Token: gitpod.F("token"), + PageSize: gitpod.F(int64(0)), + Filter: gitpod.F(gitpod.UsageListEnvironmentRuntimeRecordsParamsFilter{ + DateRange: gitpod.F(gitpod.UsageListEnvironmentRuntimeRecordsParamsFilterDateRange{ + EndTime: gitpod.F(time.Now()), + StartTime: gitpod.F(time.Now()), + }), + ProjectID: gitpod.F("d2c94c27-3b76-4a42-b88c-95a85e392c68"), + }), + Pagination: gitpod.F(gitpod.UsageListEnvironmentRuntimeRecordsParamsPagination{ + Token: gitpod.F("token"), + PageSize: gitpod.F(int64(100)), + }), + }) if err != nil { - t.Error(err) + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) } - t.Logf("%+v\n", response.OrganizationID) } diff --git a/user.go b/user.go index 5e2f4ef..82b394f 100644 --- a/user.go +++ b/user.go @@ -21,8 +21,9 @@ import ( // automatically. You should not instantiate this service directly, and instead use // the [NewUserService] method instead. type UserService struct { - Options []option.RequestOption - Pats *UserPatService + Options []option.RequestOption + Dotfiles *UserDotfileService + Pats *UserPatService } // NewUserService generates a new service that applies the given options to each @@ -31,6 +32,7 @@ type UserService struct { func NewUserService(opts ...option.RequestOption) (r *UserService) { r = &UserService{} r.Options = opts + r.Dotfiles = NewUserDotfileService(opts...) r.Pats = NewUserPatService(opts...) return } diff --git a/userdotfile.go b/userdotfile.go new file mode 100644 index 0000000..5657f0b --- /dev/null +++ b/userdotfile.go @@ -0,0 +1,146 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package gitpod + +import ( + "context" + "net/http" + + "github.com/gitpod-io/gitpod-sdk-go/internal/apijson" + "github.com/gitpod-io/gitpod-sdk-go/internal/param" + "github.com/gitpod-io/gitpod-sdk-go/internal/requestconfig" + "github.com/gitpod-io/gitpod-sdk-go/option" +) + +// UserDotfileService contains methods and other services that help with +// interacting with the gitpod 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 [NewUserDotfileService] method instead. +type UserDotfileService struct { + Options []option.RequestOption +} + +// NewUserDotfileService 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 NewUserDotfileService(opts ...option.RequestOption) (r *UserDotfileService) { + r = &UserDotfileService{} + r.Options = opts + return +} + +// Gets the dotfiles for a user. +// +// Use this method to: +// +// - Retrieve user dotfiles +// +// ### Examples +// +// - Get dotfiles: +// +// Retrieves the dotfiles for the current user. +// +// ```yaml +// {} +// ``` +func (r *UserDotfileService) Get(ctx context.Context, body UserDotfileGetParams, opts ...option.RequestOption) (res *UserDotfileGetResponse, err error) { + opts = append(r.Options[:], opts...) + path := "gitpod.v1.UserService/GetDotfilesConfiguration" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// Sets the dotfiles configuration for a user. +// +// Use this method to: +// +// - Configure user dotfiles +// - Update dotfiles settings +// +// ### Examples +// +// - Set dotfiles configuration: +// +// Sets the dotfiles configuration for the current user. +// +// ```yaml +// { "repository": "https://github.com/gitpod-io/dotfiles" } +// ``` +// +// - Remove dotfiles: +// +// Removes the dotfiles for the current user. +// +// ```yaml +// {} +// ``` +func (r *UserDotfileService) Set(ctx context.Context, body UserDotfileSetParams, opts ...option.RequestOption) (res *UserDotfileSetResponse, err error) { + opts = append(r.Options[:], opts...) + path := "gitpod.v1.UserService/SetDotfilesConfiguration" + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +type DotfilesConfiguration struct { + // The URL of a dotfiles repository. + Repository string `json:"repository" format:"uri"` + JSON dotfilesConfigurationJSON `json:"-"` +} + +// dotfilesConfigurationJSON contains the JSON metadata for the struct +// [DotfilesConfiguration] +type dotfilesConfigurationJSON struct { + Repository apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *DotfilesConfiguration) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r dotfilesConfigurationJSON) RawJSON() string { + return r.raw +} + +type UserDotfileGetResponse struct { + DotfilesConfiguration DotfilesConfiguration `json:"dotfilesConfiguration,required"` + JSON userDotfileGetResponseJSON `json:"-"` +} + +// userDotfileGetResponseJSON contains the JSON metadata for the struct +// [UserDotfileGetResponse] +type userDotfileGetResponseJSON struct { + DotfilesConfiguration apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *UserDotfileGetResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r userDotfileGetResponseJSON) RawJSON() string { + return r.raw +} + +type UserDotfileSetResponse = interface{} + +type UserDotfileGetParams struct { + Empty param.Field[bool] `json:"empty"` +} + +func (r UserDotfileGetParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +type UserDotfileSetParams struct { + Repository param.Field[string] `json:"repository" format:"uri"` +} + +func (r UserDotfileSetParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} diff --git a/userdotfile_test.go b/userdotfile_test.go new file mode 100644 index 0000000..cb85ea9 --- /dev/null +++ b/userdotfile_test.go @@ -0,0 +1,64 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package gitpod_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/gitpod-io/gitpod-sdk-go" + "github.com/gitpod-io/gitpod-sdk-go/internal/testutil" + "github.com/gitpod-io/gitpod-sdk-go/option" +) + +func TestUserDotfileGetWithOptionalParams(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 := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + _, err := client.Users.Dotfiles.Get(context.TODO(), gitpod.UserDotfileGetParams{ + Empty: gitpod.F(true), + }) + if err != nil { + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestUserDotfileSetWithOptionalParams(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 := gitpod.NewClient( + option.WithBaseURL(baseURL), + option.WithBearerToken("My Bearer Token"), + ) + _, err := client.Users.Dotfiles.Set(context.TODO(), gitpod.UserDotfileSetParams{ + Repository: gitpod.F("https://example.com"), + }) + if err != nil { + var apierr *gitpod.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +}