diff --git a/Steepfile b/Steepfile index b9e86e20..0f7fdcc2 100644 --- a/Steepfile +++ b/Steepfile @@ -169,3 +169,16 @@ target :consumers do library 'tempfile' configure_code_diagnostics Steep::Diagnostic::Ruby.all_error end + +target :sandboxes do + signature 'sig', 'sig_dev' + check 'lib/volcano/sandbox_models.rb' + check 'lib/volcano/sandbox_request.rb' + check 'lib/volcano/sandbox_response.rb' + check 'lib/volcano/sandboxes.rb' + check 'lib/volcano/sandbox_session.rb' + check 'lib/volcano/sandbox_files.rb' + library 'base64' + library 'securerandom' + configure_code_diagnostics Steep::Diagnostic::Ruby.all_error +end diff --git a/Steepfile.transport b/Steepfile.transport index 7a7de94e..0bb13239 100644 --- a/Steepfile.transport +++ b/Steepfile.transport @@ -5,6 +5,7 @@ target :client_transport do ignore_signature 'sig/client.rbs' check 'lib/volcano/client.rb' check 'lib/volcano/generated_transport.rb' + check 'lib/volcano/generated_transport_sandbox.rb' check 'lib/volcano/generated_transport_anonymous.rb' check 'lib/volcano/generated_transport_auth.rb' check 'lib/volcano/generated_transport_confirmation.rb' diff --git a/docs/README.md b/docs/README.md index 6c476574..a5d57761 100644 --- a/docs/README.md +++ b/docs/README.md @@ -129,3 +129,5 @@ See [Authentication](./authentication.md) for account, session, email, and OAuth See [Functions](./functions.md) for standard invocation, durable execution clients, and error handling. See [Versions and compatibility](./versions.md) for runtime support, upgrades, and restoring a tested dependency set. + +- [Sandboxes](sandboxes.md): isolated commands, sessions, files, and HTTP access. diff --git a/docs/sandboxes.md b/docs/sandboxes.md new file mode 100644 index 00000000..d193a61b --- /dev/null +++ b/docs/sandboxes.md @@ -0,0 +1,86 @@ +--- +title: Sandboxes +description: Run isolated commands and manage sessions, files, and HTTP services from Ruby. +--- + +Run a command from trusted backend code in an environment with Sandbox access enabled: + +```ruby +require 'volcano' +require 'securerandom' + +client = Volcano::Client.new( + anon_key: ENV.fetch('VOLCANO_ANON_KEY'), + service_key: ENV.fetch('VOLCANO_SERVICE_KEY'), + api_url: ENV.fetch('VOLCANO_API_URL', 'https://api.volcano.dev') +) +project_id = ENV.fetch('VOLCANO_PROJECT_ID') +request_id = SecureRandom.uuid +result = client.sandboxes.exec( + project_id, 'python -c "print(42)"', + region: 'aws-us-east-1', preset: 'python3.12', request_id: request_id +) +puts result.stdout +``` + +Keep the same `request_id` when retrying an uncertain create or execution. A new +ID represents a new operation. The SDK does not replay commands after transport failures. A rejected user token +is refreshed once; the retry preserves the request ID. +Nonzero exits and timeouts are result fields, not API exceptions. API failures +raise typed `Volcano::Error` exceptions with `status`, `code`, and `retry_after` +when supplied by the server. + +## Keep a session + +```ruby +client.sandboxes.create( + project_id, region: 'aws-us-east-1', preset: 'python3.12', max_duration_seconds: 300 +).use do |session| + 60.times do + break if session.refresh.state == 'running' + sleep 1 + end + raise 'Sandbox did not become ready' unless session.state == 'running' + + bytes = (0..255).to_a.pack('C*') + session.files.write('/tmp/input.bin', bytes) + raise 'File changed' unless session.files.read('/tmp/input.bin') == bytes + puts session.exec('wc -c /tmp/input.bin').stdout +end +``` + +Creation, suspension, resumption, and termination are asynchronous. Use `refresh` +to observe state. `use` requests termination when its block exits, including on +exceptions. If cleanup also fails, the original block exception is preserved. +It does not wait for termination to complete. Use `get(session_id)` +to reconnect, then `suspend`, `resume`, or `terminate` as needed. Files preserve +binary `String` content. Writes accept at most 8 MiB. + +## Access a background HTTP service + +Inside a running session, detach the service and redirect its streams: + +```ruby +session.exec('nohup python -m http.server 8080 --bind 0.0.0.0 >/tmp/http.log 2>&1 { 'YOUR TOKEN GETTER PROC' } + + # Configure Bearer authorization (opaque): ProjectAccessToken config.access_token = 'YOUR_BEARER_TOKEN' # Configure a proc to get access tokens in lieu of the static access_token configuration config.access_token_getter = -> { 'YOUR TOKEN GETTER PROC' } @@ -203,15 +208,19 @@ Class | Method | HTTP request | Description *Volcano::Generated::DurableFunctionsApi* | [**update_durable_function_scheduler**](docs/DurableFunctionsApi.md#update_durable_function_scheduler) | **PATCH** /projects/{id}/durable-functions/{functionId}/schedulers/{schedulerId} | Update a durable function scheduler *Volcano::Generated::FrontendsApi* | [**create_frontend**](docs/FrontendsApi.md#create_frontend) | **POST** /projects/{id}/frontends | Create a new frontend deployment *Volcano::Generated::FrontendsApi* | [**create_frontend_custom_domain**](docs/FrontendsApi.md#create_frontend_custom_domain) | **POST** /projects/{id}/frontends/{frontendId}/domain | Configure frontend custom domain (PRO) +*Volcano::Generated::FrontendsApi* | [**create_frontend_function_route**](docs/FrontendsApi.md#create_frontend_function_route) | **POST** /projects/{id}/frontends/{frontendId}/function-routes | Route a Frontend path to an HTTP Function *Volcano::Generated::FrontendsApi* | [**delete_frontend**](docs/FrontendsApi.md#delete_frontend) | **DELETE** /projects/{id}/frontends/{frontendId} | Delete a frontend *Volcano::Generated::FrontendsApi* | [**delete_frontend_custom_domain**](docs/FrontendsApi.md#delete_frontend_custom_domain) | **DELETE** /projects/{id}/frontends/{frontendId}/domain | Delete frontend custom domain +*Volcano::Generated::FrontendsApi* | [**delete_frontend_function_route**](docs/FrontendsApi.md#delete_frontend_function_route) | **DELETE** /projects/{id}/frontends/{frontendId}/function-routes/{routeId} | Delete a Frontend Function route *Volcano::Generated::FrontendsApi* | [**get_frontend**](docs/FrontendsApi.md#get_frontend) | **GET** /projects/{id}/frontends/{frontendId} | Get frontend details *Volcano::Generated::FrontendsApi* | [**get_frontend_custom_domain**](docs/FrontendsApi.md#get_frontend_custom_domain) | **GET** /projects/{id}/frontends/{frontendId}/domain | Get frontend custom domain status *Volcano::Generated::FrontendsApi* | [**get_frontend_usage_history**](docs/FrontendsApi.md#get_frontend_usage_history) | **GET** /projects/{id}/frontends/{frontendId}/usage | Per-day request and error counts for a single frontend *Volcano::Generated::FrontendsApi* | [**list_frontend_deployments**](docs/FrontendsApi.md#list_frontend_deployments) | **GET** /projects/{id}/frontends/{frontendId}/deployments | List frontend deployments +*Volcano::Generated::FrontendsApi* | [**list_frontend_function_routes**](docs/FrontendsApi.md#list_frontend_function_routes) | **GET** /projects/{id}/frontends/{frontendId}/function-routes | List a Frontend's Function routes *Volcano::Generated::FrontendsApi* | [**list_frontends**](docs/FrontendsApi.md#list_frontends) | **GET** /projects/{id}/frontends | List all frontends in a project *Volcano::Generated::FrontendsApi* | [**list_project_custom_domains**](docs/FrontendsApi.md#list_project_custom_domains) | **GET** /projects/{id}/domains | List all custom domains in a project *Volcano::Generated::FrontendsApi* | [**redeploy_frontend**](docs/FrontendsApi.md#redeploy_frontend) | **POST** /projects/{id}/frontends/{frontendId}/redeploy | Redeploy frontend using latest uploaded artifact +*Volcano::Generated::FrontendsApi* | [**update_frontend_function_route**](docs/FrontendsApi.md#update_frontend_function_route) | **PUT** /projects/{id}/frontends/{frontendId}/function-routes/{routeId} | Replace a Frontend Function route *Volcano::Generated::FunctionsApi* | [**create_function**](docs/FunctionsApi.md#create_function) | **POST** /projects/{id}/functions | Create or update function code *Volcano::Generated::FunctionsApi* | [**create_function_scheduler**](docs/FunctionsApi.md#create_function_scheduler) | **POST** /projects/{id}/functions/{functionId}/schedulers | Create a scheduler for a function *Volcano::Generated::FunctionsApi* | [**create_functions_batch**](docs/FunctionsApi.md#create_functions_batch) | **POST** /projects/{id}/functions/batch | Deploy multiple functions in one request @@ -271,6 +280,12 @@ Class | Method | HTTP request | Description *Volcano::Generated::OAuthConfigurationApi* | [**list_available_o_auth_providers**](docs/OAuthConfigurationApi.md#list_available_o_auth_providers) | **GET** /projects/{id}/oauth/providers | List available OAuth providers *Volcano::Generated::OAuthConfigurationApi* | [**list_o_auth_configs**](docs/OAuthConfigurationApi.md#list_o_auth_configs) | **GET** /projects/{id}/oauth/configs | List OAuth configurations *Volcano::Generated::OAuthConfigurationApi* | [**update_o_auth_config**](docs/OAuthConfigurationApi.md#update_o_auth_config) | **PUT** /projects/{id}/oauth/configs/{provider} | Update OAuth configuration +*Volcano::Generated::ProjectAccessTokensApi* | [**create_project_access_token**](docs/ProjectAccessTokensApi.md#create_project_access_token) | **POST** /projects/{id}/access-tokens | Create a project access token +*Volcano::Generated::ProjectAccessTokensApi* | [**get_project_access_token**](docs/ProjectAccessTokensApi.md#get_project_access_token) | **GET** /projects/{id}/access-tokens/{tokenId} | Get a project access token +*Volcano::Generated::ProjectAccessTokensApi* | [**get_project_access_token_usage**](docs/ProjectAccessTokensApi.md#get_project_access_token_usage) | **GET** /projects/{id}/access-tokens/{tokenId}/usage | Per-day request counts for one access token +*Volcano::Generated::ProjectAccessTokensApi* | [**list_project_access_tokens**](docs/ProjectAccessTokensApi.md#list_project_access_tokens) | **GET** /projects/{id}/access-tokens | List a project's access tokens +*Volcano::Generated::ProjectAccessTokensApi* | [**list_project_access_tokens_usage**](docs/ProjectAccessTokensApi.md#list_project_access_tokens_usage) | **GET** /projects/{id}/access-tokens/usage | Per-day request counts for every access token in a project +*Volcano::Generated::ProjectAccessTokensApi* | [**revoke_project_access_token**](docs/ProjectAccessTokensApi.md#revoke_project_access_token) | **DELETE** /projects/{id}/access-tokens/{tokenId} | Revoke a project access token *Volcano::Generated::ProjectImportsApi* | [**complete_import_connect**](docs/ProjectImportsApi.md#complete_import_connect) | **GET** /imports/{provider}/callback | Complete a project import provider connection *Volcano::Generated::ProjectImportsApi* | [**delete_import_connection**](docs/ProjectImportsApi.md#delete_import_connection) | **DELETE** /user/imports/connections/{connectionId} | Delete a project import provider connection *Volcano::Generated::ProjectImportsApi* | [**get_project_import_run**](docs/ProjectImportsApi.md#get_project_import_run) | **GET** /imports/{provider}/runs/{runId} | Get a project import run @@ -299,6 +314,7 @@ Class | Method | HTTP request | Description *Volcano::Generated::ProjectsApi* | [**list_project_deployments**](docs/ProjectsApi.md#list_project_deployments) | **GET** /projects/{id}/deployments | List deployments in a project *Volcano::Generated::ProjectsApi* | [**list_projects**](docs/ProjectsApi.md#list_projects) | **GET** /projects | List all projects for authenticated user *Volcano::Generated::ProjectsApi* | [**query_project_metrics**](docs/ProjectsApi.md#query_project_metrics) | **POST** /projects/{id}/metrics/query | Query project runtime metrics +*Volcano::Generated::ProjectsApi* | [**replace_frontend_shared_variables**](docs/ProjectsApi.md#replace_frontend_shared_variables) | **PUT** /projects/{id}/frontend-shared-variables | Replace frontend shared variable names *Volcano::Generated::ProjectsApi* | [**replace_shared_variables**](docs/ProjectsApi.md#replace_shared_variables) | **PUT** /projects/{id}/shared-variables | Replace shared variable names *Volcano::Generated::ProjectsApi* | [**set_project_git_production_branch**](docs/ProjectsApi.md#set_project_git_production_branch) | **PUT** /projects/{id}/git-connection/production-branch | Set the branch a project deploys from *Volcano::Generated::ProjectsApi* | [**summarize_project_deployments**](docs/ProjectsApi.md#summarize_project_deployments) | **GET** /projects/{id}/deployments/summary | Summarize deployments in a project @@ -308,6 +324,26 @@ Class | Method | HTTP request | Description *Volcano::Generated::RealtimeApi* | [**get_realtime_config**](docs/RealtimeApi.md#get_realtime_config) | **GET** /projects/{id}/realtime/config | Get realtime configuration for a project *Volcano::Generated::RealtimeApi* | [**get_realtime_stats**](docs/RealtimeApi.md#get_realtime_stats) | **GET** /projects/{id}/realtime/stats | Get realtime statistics for a project *Volcano::Generated::RealtimeApi* | [**update_realtime_config**](docs/RealtimeApi.md#update_realtime_config) | **PUT** /projects/{id}/realtime/config | Update realtime configuration for a project +*Volcano::Generated::SandboxesApi* | [**create_sandbox**](docs/SandboxesApi.md#create_sandbox) | **POST** /projects/{id}/sandboxes | Create a sandbox template from a verified preset +*Volcano::Generated::SandboxesApi* | [**create_sandbox_session**](docs/SandboxesApi.md#create_sandbox_session) | **POST** /projects/{id}/sandbox-sessions | Start a sandbox session +*Volcano::Generated::SandboxesApi* | [**create_sandbox_session_access**](docs/SandboxesApi.md#create_sandbox_session_access) | **POST** /sandbox-sessions/{sessionId}/access | Issue a short-lived port-scoped access credential +*Volcano::Generated::SandboxesApi* | [**delete_sandbox**](docs/SandboxesApi.md#delete_sandbox) | **DELETE** /projects/{id}/sandboxes/{sandboxId} | Retire a template and terminate its sessions +*Volcano::Generated::SandboxesApi* | [**execute_sandbox**](docs/SandboxesApi.md#execute_sandbox) | **POST** /projects/{id}/sandbox-executions | Execute once and return after confirmed termination +*Volcano::Generated::SandboxesApi* | [**execute_sandbox_session**](docs/SandboxesApi.md#execute_sandbox_session) | **POST** /sandbox-sessions/{sessionId}/exec | Execute a command within a session +*Volcano::Generated::SandboxesApi* | [**get_sandbox**](docs/SandboxesApi.md#get_sandbox) | **GET** /projects/{id}/sandboxes/{sandboxId} | Get a sandbox template +*Volcano::Generated::SandboxesApi* | [**get_sandbox_session**](docs/SandboxesApi.md#get_sandbox_session) | **GET** /sandbox-sessions/{sessionId} | Get a sandbox session +*Volcano::Generated::SandboxesApi* | [**grant_sandbox_session**](docs/SandboxesApi.md#grant_sandbox_session) | **PUT** /sandbox-sessions/{sessionId}/grants/{subjectId} | Authorize an authenticated project user for this session +*Volcano::Generated::SandboxesApi* | [**list_sandbox_deployments**](docs/SandboxesApi.md#list_sandbox_deployments) | **GET** /projects/{id}/sandboxes/{sandboxId}/deployments | List sandbox deployment history +*Volcano::Generated::SandboxesApi* | [**list_sandbox_presets**](docs/SandboxesApi.md#list_sandbox_presets) | **GET** /sandboxes/presets | List available sandbox presets +*Volcano::Generated::SandboxesApi* | [**list_sandbox_sessions**](docs/SandboxesApi.md#list_sandbox_sessions) | **GET** /projects/{id}/sandbox-sessions | List project sandbox sessions +*Volcano::Generated::SandboxesApi* | [**list_sandboxes**](docs/SandboxesApi.md#list_sandboxes) | **GET** /projects/{id}/sandboxes | List sandbox templates +*Volcano::Generated::SandboxesApi* | [**read_sandbox_session_file**](docs/SandboxesApi.md#read_sandbox_session_file) | **POST** /sandbox-sessions/{sessionId}/files/read | Read a workspace file +*Volcano::Generated::SandboxesApi* | [**resume_sandbox_session**](docs/SandboxesApi.md#resume_sandbox_session) | **POST** /sandbox-sessions/{sessionId}/resume | Resume a sandbox session +*Volcano::Generated::SandboxesApi* | [**revoke_sandbox_session**](docs/SandboxesApi.md#revoke_sandbox_session) | **DELETE** /sandbox-sessions/{sessionId}/grants/{subjectId} | Revoke a project user session grant +*Volcano::Generated::SandboxesApi* | [**suspend_sandbox_session**](docs/SandboxesApi.md#suspend_sandbox_session) | **POST** /sandbox-sessions/{sessionId}/suspend | Suspend a sandbox session +*Volcano::Generated::SandboxesApi* | [**terminate_sandbox_session**](docs/SandboxesApi.md#terminate_sandbox_session) | **DELETE** /sandbox-sessions/{sessionId} | Request sandbox termination +*Volcano::Generated::SandboxesApi* | [**update_sandbox**](docs/SandboxesApi.md#update_sandbox) | **PATCH** /projects/{id}/sandboxes/{sandboxId} | Rename a sandbox template +*Volcano::Generated::SandboxesApi* | [**write_sandbox_session_file**](docs/SandboxesApi.md#write_sandbox_session_file) | **POST** /sandbox-sessions/{sessionId}/files/write | Write a workspace file *Volcano::Generated::ServiceKeysApi* | [**create_service_key**](docs/ServiceKeysApi.md#create_service_key) | **POST** /projects/{id}/service-keys | Create service key *Volcano::Generated::ServiceKeysApi* | [**delete_service_key**](docs/ServiceKeysApi.md#delete_service_key) | **DELETE** /projects/{id}/service-keys/{keyId} | Delete service key *Volcano::Generated::ServiceKeysApi* | [**get_service_key**](docs/ServiceKeysApi.md#get_service_key) | **GET** /projects/{id}/service-keys/{keyId} | Get service key @@ -332,6 +368,11 @@ Class | Method | HTTP request | Description *Volcano::Generated::StoragePoliciesApi* | [**create_storage_policy**](docs/StoragePoliciesApi.md#create_storage_policy) | **POST** /projects/{id}/storage/buckets/{bucketName}/policies | Create a storage policy *Volcano::Generated::StoragePoliciesApi* | [**delete_storage_policy**](docs/StoragePoliciesApi.md#delete_storage_policy) | **DELETE** /projects/{id}/storage/buckets/{bucketName}/policies/{policyId} | Delete a storage policy *Volcano::Generated::StoragePoliciesApi* | [**list_storage_policies**](docs/StoragePoliciesApi.md#list_storage_policies) | **GET** /projects/{id}/storage/buckets/{bucketName}/policies | List storage policies for a bucket +*Volcano::Generated::SystemApi* | [**call_mcp**](docs/SystemApi.md#call_mcp) | **POST** /mcp | Model Context Protocol endpoint +*Volcano::Generated::SystemApi* | [**get_open_api_spec_json**](docs/SystemApi.md#get_open_api_spec_json) | **GET** /openapi.json | Fetch the OpenAPI specification as JSON +*Volcano::Generated::SystemApi* | [**get_open_api_spec_yaml**](docs/SystemApi.md#get_open_api_spec_yaml) | **GET** /openapi.yaml | Fetch the OpenAPI specification as YAML +*Volcano::Generated::SystemApi* | [**head_open_api_spec_json**](docs/SystemApi.md#head_open_api_spec_json) | **HEAD** /openapi.json | Check the JSON OpenAPI specification +*Volcano::Generated::SystemApi* | [**head_open_api_spec_yaml**](docs/SystemApi.md#head_open_api_spec_yaml) | **HEAD** /openapi.yaml | Check the YAML OpenAPI specification *Volcano::Generated::SystemApi* | [**health_check**](docs/SystemApi.md#health_check) | **GET** /health | Health check endpoint *Volcano::Generated::VariablesApi* | [**create_variable**](docs/VariablesApi.md#create_variable) | **POST** /projects/{id}/variables | Create or update a variable *Volcano::Generated::VariablesApi* | [**delete_variable**](docs/VariablesApi.md#delete_variable) | **DELETE** /projects/{id}/variables/{name} | Delete a variable @@ -402,6 +443,11 @@ Class | Method | HTTP request | Description - [Volcano::Generated::BanUserResponse](docs/BanUserResponse.md) - [Volcano::Generated::BatchFunctionDeployFailure](docs/BatchFunctionDeployFailure.md) - [Volcano::Generated::BatchFunctionDeployResponse](docs/BatchFunctionDeployResponse.md) + - [Volcano::Generated::CallMCP200Response](docs/CallMCP200Response.md) + - [Volcano::Generated::CallMCP200ResponseError](docs/CallMCP200ResponseError.md) + - [Volcano::Generated::CallMCP200ResponseId](docs/CallMCP200ResponseId.md) + - [Volcano::Generated::CallMCPRequest](docs/CallMCPRequest.md) + - [Volcano::Generated::CallMCPRequestId](docs/CallMCPRequestId.md) - [Volcano::Generated::CallOAuthProviderAPI200Response](docs/CallOAuthProviderAPI200Response.md) - [Volcano::Generated::CallOAuthProviderAPIRequest](docs/CallOAuthProviderAPIRequest.md) - [Volcano::Generated::CompleteUploadSessionResponse](docs/CompleteUploadSessionResponse.md) @@ -415,15 +461,20 @@ Class | Method | HTTP request | Description - [Volcano::Generated::CreateDatabaseRestoreRequest](docs/CreateDatabaseRestoreRequest.md) - [Volcano::Generated::CreateEmailTemplateRequest](docs/CreateEmailTemplateRequest.md) - [Volcano::Generated::CreateFrontendCustomDomainRequest](docs/CreateFrontendCustomDomainRequest.md) + - [Volcano::Generated::CreateFrontendFunctionRouteRequest](docs/CreateFrontendFunctionRouteRequest.md) - [Volcano::Generated::CreateFunctionSchedulerRequest](docs/CreateFunctionSchedulerRequest.md) - [Volcano::Generated::CreateOAuthConfigRequest](docs/CreateOAuthConfigRequest.md) + - [Volcano::Generated::CreateProjectAccessTokenRequest](docs/CreateProjectAccessTokenRequest.md) - [Volcano::Generated::CreateProjectRequest](docs/CreateProjectRequest.md) + - [Volcano::Generated::CreateSandboxSessionRequest](docs/CreateSandboxSessionRequest.md) + - [Volcano::Generated::CreateSandboxTemplateRequest](docs/CreateSandboxTemplateRequest.md) - [Volcano::Generated::CreateServiceKeyRequest](docs/CreateServiceKeyRequest.md) - [Volcano::Generated::CreateStorageBucketRequest](docs/CreateStorageBucketRequest.md) - [Volcano::Generated::CreateStoragePolicyRequest](docs/CreateStoragePolicyRequest.md) - [Volcano::Generated::CreateUploadSessionRequest](docs/CreateUploadSessionRequest.md) - [Volcano::Generated::CreateUploadSessionResponse](docs/CreateUploadSessionResponse.md) - [Volcano::Generated::CreateVariableRequest](docs/CreateVariableRequest.md) + - [Volcano::Generated::CreatedProjectAccessToken](docs/CreatedProjectAccessToken.md) - [Volcano::Generated::Database](docs/Database.md) - [Volcano::Generated::DatabaseBackup](docs/DatabaseBackup.md) - [Volcano::Generated::DatabaseBackupList](docs/DatabaseBackupList.md) @@ -469,6 +520,8 @@ Class | Method | HTTP request | Description - [Volcano::Generated::FrontendDeployment](docs/FrontendDeployment.md) - [Volcano::Generated::FrontendDomainRoutingRecord](docs/FrontendDomainRoutingRecord.md) - [Volcano::Generated::FrontendDomainVerificationRecord](docs/FrontendDomainVerificationRecord.md) + - [Volcano::Generated::FrontendFunctionRoute](docs/FrontendFunctionRoute.md) + - [Volcano::Generated::FrontendFunctionRouteList](docs/FrontendFunctionRouteList.md) - [Volcano::Generated::FrontendUsageDailyEntry](docs/FrontendUsageDailyEntry.md) - [Volcano::Generated::FrontendUsageData](docs/FrontendUsageData.md) - [Volcano::Generated::FrontendUsageHistoryResponse](docs/FrontendUsageHistoryResponse.md) @@ -543,6 +596,7 @@ Class | Method | HTTP request | Description - [Volcano::Generated::PaginatedFrontends](docs/PaginatedFrontends.md) - [Volcano::Generated::PaginatedFunctionDeployments](docs/PaginatedFunctionDeployments.md) - [Volcano::Generated::PaginatedFunctions](docs/PaginatedFunctions.md) + - [Volcano::Generated::PaginatedProjectAccessTokens](docs/PaginatedProjectAccessTokens.md) - [Volcano::Generated::PaginatedProjectCustomDomains](docs/PaginatedProjectCustomDomains.md) - [Volcano::Generated::PaginatedProjectDeployments](docs/PaginatedProjectDeployments.md) - [Volcano::Generated::PaginatedProjects](docs/PaginatedProjects.md) @@ -553,6 +607,10 @@ Class | Method | HTTP request | Description - [Volcano::Generated::PreviewAuthPageRequest](docs/PreviewAuthPageRequest.md) - [Volcano::Generated::PreviewAuthPageResponse](docs/PreviewAuthPageResponse.md) - [Volcano::Generated::Project](docs/Project.md) + - [Volcano::Generated::ProjectAccessToken](docs/ProjectAccessToken.md) + - [Volcano::Generated::ProjectAccessTokenScope](docs/ProjectAccessTokenScope.md) + - [Volcano::Generated::ProjectAccessTokenUsage](docs/ProjectAccessTokenUsage.md) + - [Volcano::Generated::ProjectAccessTokenUsageDailyEntry](docs/ProjectAccessTokenUsageDailyEntry.md) - [Volcano::Generated::ProjectConfig](docs/ProjectConfig.md) - [Volcano::Generated::ProjectConfigApplyResult](docs/ProjectConfigApplyResult.md) - [Volcano::Generated::ProjectConfigApplyResultEntry](docs/ProjectConfigApplyResultEntry.md) @@ -582,6 +640,7 @@ Class | Method | HTTP request | Description - [Volcano::Generated::ProjectConfigEmailTemplate](docs/ProjectConfigEmailTemplate.md) - [Volcano::Generated::ProjectConfigEmailTemplates](docs/ProjectConfigEmailTemplates.md) - [Volcano::Generated::ProjectConfigFrontend](docs/ProjectConfigFrontend.md) + - [Volcano::Generated::ProjectConfigFrontendFunctionRoute](docs/ProjectConfigFrontendFunctionRoute.md) - [Volcano::Generated::ProjectConfigFunction](docs/ProjectConfigFunction.md) - [Volcano::Generated::ProjectConfigHostedPage](docs/ProjectConfigHostedPage.md) - [Volcano::Generated::ProjectConfigHostedPages](docs/ProjectConfigHostedPages.md) @@ -648,14 +707,37 @@ Class | Method | HTTP request | Description - [Volcano::Generated::ProjectSourceExportSkip](docs/ProjectSourceExportSkip.md) - [Volcano::Generated::ProjectSourceExportState](docs/ProjectSourceExportState.md) - [Volcano::Generated::ProjectUsageResponse](docs/ProjectUsageResponse.md) + - [Volcano::Generated::PublishSandboxPresetRequest](docs/PublishSandboxPresetRequest.md) - [Volcano::Generated::RealtimeConfig](docs/RealtimeConfig.md) - [Volcano::Generated::RealtimePlanLimits](docs/RealtimePlanLimits.md) - [Volcano::Generated::RealtimeStats](docs/RealtimeStats.md) - [Volcano::Generated::RefreshOAuthProviderToken200Response](docs/RefreshOAuthProviderToken200Response.md) + - [Volcano::Generated::ReplaceFrontendSharedVariablesRequest](docs/ReplaceFrontendSharedVariablesRequest.md) - [Volcano::Generated::ReplaceSharedVariablesRequest](docs/ReplaceSharedVariablesRequest.md) - [Volcano::Generated::ResetDatabasePassword200Response](docs/ResetDatabasePassword200Response.md) - [Volcano::Generated::ResolveFunctionResponse](docs/ResolveFunctionResponse.md) - [Volcano::Generated::ResourceReference](docs/ResourceReference.md) + - [Volcano::Generated::SandboxAccess](docs/SandboxAccess.md) + - [Volcano::Generated::SandboxAccessRequest](docs/SandboxAccessRequest.md) + - [Volcano::Generated::SandboxCapacity](docs/SandboxCapacity.md) + - [Volcano::Generated::SandboxCapacityList](docs/SandboxCapacityList.md) + - [Volcano::Generated::SandboxCommandRequest](docs/SandboxCommandRequest.md) + - [Volcano::Generated::SandboxCommandResult](docs/SandboxCommandResult.md) + - [Volcano::Generated::SandboxDeployment](docs/SandboxDeployment.md) + - [Volcano::Generated::SandboxDeploymentPage](docs/SandboxDeploymentPage.md) + - [Volcano::Generated::SandboxExecutionRequest](docs/SandboxExecutionRequest.md) + - [Volcano::Generated::SandboxExecutionResult](docs/SandboxExecutionResult.md) + - [Volcano::Generated::SandboxFileReadRequest](docs/SandboxFileReadRequest.md) + - [Volcano::Generated::SandboxFileResult](docs/SandboxFileResult.md) + - [Volcano::Generated::SandboxFileWriteRequest](docs/SandboxFileWriteRequest.md) + - [Volcano::Generated::SandboxPagination](docs/SandboxPagination.md) + - [Volcano::Generated::SandboxPreset](docs/SandboxPreset.md) + - [Volcano::Generated::SandboxPresetList](docs/SandboxPresetList.md) + - [Volcano::Generated::SandboxSession](docs/SandboxSession.md) + - [Volcano::Generated::SandboxSessionPage](docs/SandboxSessionPage.md) + - [Volcano::Generated::SandboxSubjectGrantRequest](docs/SandboxSubjectGrantRequest.md) + - [Volcano::Generated::SandboxTemplate](docs/SandboxTemplate.md) + - [Volcano::Generated::SandboxTemplatePage](docs/SandboxTemplatePage.md) - [Volcano::Generated::ScheduleRequest](docs/ScheduleRequest.md) - [Volcano::Generated::ServiceKey](docs/ServiceKey.md) - [Volcano::Generated::SetProjectGitProductionBranchRequest](docs/SetProjectGitProductionBranchRequest.md) @@ -684,6 +766,7 @@ Class | Method | HTTP request | Description - [Volcano::Generated::UpdateProjectGitDeploySettingsRequest](docs/UpdateProjectGitDeploySettingsRequest.md) - [Volcano::Generated::UpdateProjectRequest](docs/UpdateProjectRequest.md) - [Volcano::Generated::UpdateRealtimeConfigRequest](docs/UpdateRealtimeConfigRequest.md) + - [Volcano::Generated::UpdateSandboxTemplateRequest](docs/UpdateSandboxTemplateRequest.md) - [Volcano::Generated::UpdateStorageBucketRequest](docs/UpdateStorageBucketRequest.md) - [Volcano::Generated::UpdateVariableRequest](docs/UpdateVariableRequest.md) - [Volcano::Generated::UploadSessionPart](docs/UploadSessionPart.md) @@ -711,5 +794,9 @@ Authentication schemes defined for the API: ### UserToken -- **Type**: Bearer authentication (JWT) +- **Type**: Bearer authentication (opaque) + +### ProjectAccessToken + +- **Type**: Bearer authentication (opaque) diff --git a/lib/volcano/generated/lib/volcano-generated.rb b/lib/volcano/generated/lib/volcano-generated.rb index 97b4f52b..dd0fe352 100644 --- a/lib/volcano/generated/lib/volcano-generated.rb +++ b/lib/volcano/generated/lib/volcano-generated.rb @@ -78,6 +78,11 @@ Volcano::Generated.autoload :BanUserResponse, 'volcano-generated/models/ban_user_response' Volcano::Generated.autoload :BatchFunctionDeployFailure, 'volcano-generated/models/batch_function_deploy_failure' Volcano::Generated.autoload :BatchFunctionDeployResponse, 'volcano-generated/models/batch_function_deploy_response' +Volcano::Generated.autoload :CallMCP200Response, 'volcano-generated/models/call_mcp200_response' +Volcano::Generated.autoload :CallMCP200ResponseError, 'volcano-generated/models/call_mcp200_response_error' +Volcano::Generated.autoload :CallMCP200ResponseId, 'volcano-generated/models/call_mcp200_response_id' +Volcano::Generated.autoload :CallMCPRequest, 'volcano-generated/models/call_mcp_request' +Volcano::Generated.autoload :CallMCPRequestId, 'volcano-generated/models/call_mcp_request_id' Volcano::Generated.autoload :CallOAuthProviderAPI200Response, 'volcano-generated/models/call_o_auth_provider_api200_response' Volcano::Generated.autoload :CallOAuthProviderAPIRequest, 'volcano-generated/models/call_o_auth_provider_api_request' Volcano::Generated.autoload :CompleteUploadSessionResponse, 'volcano-generated/models/complete_upload_session_response' @@ -91,15 +96,20 @@ Volcano::Generated.autoload :CreateDatabaseRestoreRequest, 'volcano-generated/models/create_database_restore_request' Volcano::Generated.autoload :CreateEmailTemplateRequest, 'volcano-generated/models/create_email_template_request' Volcano::Generated.autoload :CreateFrontendCustomDomainRequest, 'volcano-generated/models/create_frontend_custom_domain_request' +Volcano::Generated.autoload :CreateFrontendFunctionRouteRequest, 'volcano-generated/models/create_frontend_function_route_request' Volcano::Generated.autoload :CreateFunctionSchedulerRequest, 'volcano-generated/models/create_function_scheduler_request' Volcano::Generated.autoload :CreateOAuthConfigRequest, 'volcano-generated/models/create_o_auth_config_request' +Volcano::Generated.autoload :CreateProjectAccessTokenRequest, 'volcano-generated/models/create_project_access_token_request' Volcano::Generated.autoload :CreateProjectRequest, 'volcano-generated/models/create_project_request' +Volcano::Generated.autoload :CreateSandboxSessionRequest, 'volcano-generated/models/create_sandbox_session_request' +Volcano::Generated.autoload :CreateSandboxTemplateRequest, 'volcano-generated/models/create_sandbox_template_request' Volcano::Generated.autoload :CreateServiceKeyRequest, 'volcano-generated/models/create_service_key_request' Volcano::Generated.autoload :CreateStorageBucketRequest, 'volcano-generated/models/create_storage_bucket_request' Volcano::Generated.autoload :CreateStoragePolicyRequest, 'volcano-generated/models/create_storage_policy_request' Volcano::Generated.autoload :CreateUploadSessionRequest, 'volcano-generated/models/create_upload_session_request' Volcano::Generated.autoload :CreateUploadSessionResponse, 'volcano-generated/models/create_upload_session_response' Volcano::Generated.autoload :CreateVariableRequest, 'volcano-generated/models/create_variable_request' +Volcano::Generated.autoload :CreatedProjectAccessToken, 'volcano-generated/models/created_project_access_token' Volcano::Generated.autoload :Database, 'volcano-generated/models/database' Volcano::Generated.autoload :DatabaseBackup, 'volcano-generated/models/database_backup' Volcano::Generated.autoload :DatabaseBackupList, 'volcano-generated/models/database_backup_list' @@ -145,6 +155,8 @@ Volcano::Generated.autoload :FrontendDeployment, 'volcano-generated/models/frontend_deployment' Volcano::Generated.autoload :FrontendDomainRoutingRecord, 'volcano-generated/models/frontend_domain_routing_record' Volcano::Generated.autoload :FrontendDomainVerificationRecord, 'volcano-generated/models/frontend_domain_verification_record' +Volcano::Generated.autoload :FrontendFunctionRoute, 'volcano-generated/models/frontend_function_route' +Volcano::Generated.autoload :FrontendFunctionRouteList, 'volcano-generated/models/frontend_function_route_list' Volcano::Generated.autoload :FrontendUsageDailyEntry, 'volcano-generated/models/frontend_usage_daily_entry' Volcano::Generated.autoload :FrontendUsageData, 'volcano-generated/models/frontend_usage_data' Volcano::Generated.autoload :FrontendUsageHistoryResponse, 'volcano-generated/models/frontend_usage_history_response' @@ -219,6 +231,7 @@ Volcano::Generated.autoload :PaginatedFrontends, 'volcano-generated/models/paginated_frontends' Volcano::Generated.autoload :PaginatedFunctionDeployments, 'volcano-generated/models/paginated_function_deployments' Volcano::Generated.autoload :PaginatedFunctions, 'volcano-generated/models/paginated_functions' +Volcano::Generated.autoload :PaginatedProjectAccessTokens, 'volcano-generated/models/paginated_project_access_tokens' Volcano::Generated.autoload :PaginatedProjectCustomDomains, 'volcano-generated/models/paginated_project_custom_domains' Volcano::Generated.autoload :PaginatedProjectDeployments, 'volcano-generated/models/paginated_project_deployments' Volcano::Generated.autoload :PaginatedProjects, 'volcano-generated/models/paginated_projects' @@ -229,6 +242,10 @@ Volcano::Generated.autoload :PreviewAuthPageRequest, 'volcano-generated/models/preview_auth_page_request' Volcano::Generated.autoload :PreviewAuthPageResponse, 'volcano-generated/models/preview_auth_page_response' Volcano::Generated.autoload :Project, 'volcano-generated/models/project' +Volcano::Generated.autoload :ProjectAccessToken, 'volcano-generated/models/project_access_token' +Volcano::Generated.autoload :ProjectAccessTokenScope, 'volcano-generated/models/project_access_token_scope' +Volcano::Generated.autoload :ProjectAccessTokenUsage, 'volcano-generated/models/project_access_token_usage' +Volcano::Generated.autoload :ProjectAccessTokenUsageDailyEntry, 'volcano-generated/models/project_access_token_usage_daily_entry' Volcano::Generated.autoload :ProjectConfig, 'volcano-generated/models/project_config' Volcano::Generated.autoload :ProjectConfigApplyResult, 'volcano-generated/models/project_config_apply_result' Volcano::Generated.autoload :ProjectConfigApplyResultEntry, 'volcano-generated/models/project_config_apply_result_entry' @@ -258,6 +275,7 @@ Volcano::Generated.autoload :ProjectConfigEmailTemplate, 'volcano-generated/models/project_config_email_template' Volcano::Generated.autoload :ProjectConfigEmailTemplates, 'volcano-generated/models/project_config_email_templates' Volcano::Generated.autoload :ProjectConfigFrontend, 'volcano-generated/models/project_config_frontend' +Volcano::Generated.autoload :ProjectConfigFrontendFunctionRoute, 'volcano-generated/models/project_config_frontend_function_route' Volcano::Generated.autoload :ProjectConfigFunction, 'volcano-generated/models/project_config_function' Volcano::Generated.autoload :ProjectConfigHostedPage, 'volcano-generated/models/project_config_hosted_page' Volcano::Generated.autoload :ProjectConfigHostedPages, 'volcano-generated/models/project_config_hosted_pages' @@ -324,14 +342,37 @@ Volcano::Generated.autoload :ProjectSourceExportSkip, 'volcano-generated/models/project_source_export_skip' Volcano::Generated.autoload :ProjectSourceExportState, 'volcano-generated/models/project_source_export_state' Volcano::Generated.autoload :ProjectUsageResponse, 'volcano-generated/models/project_usage_response' +Volcano::Generated.autoload :PublishSandboxPresetRequest, 'volcano-generated/models/publish_sandbox_preset_request' Volcano::Generated.autoload :RealtimeConfig, 'volcano-generated/models/realtime_config' Volcano::Generated.autoload :RealtimePlanLimits, 'volcano-generated/models/realtime_plan_limits' Volcano::Generated.autoload :RealtimeStats, 'volcano-generated/models/realtime_stats' Volcano::Generated.autoload :RefreshOAuthProviderToken200Response, 'volcano-generated/models/refresh_o_auth_provider_token200_response' +Volcano::Generated.autoload :ReplaceFrontendSharedVariablesRequest, 'volcano-generated/models/replace_frontend_shared_variables_request' Volcano::Generated.autoload :ReplaceSharedVariablesRequest, 'volcano-generated/models/replace_shared_variables_request' Volcano::Generated.autoload :ResetDatabasePassword200Response, 'volcano-generated/models/reset_database_password200_response' Volcano::Generated.autoload :ResolveFunctionResponse, 'volcano-generated/models/resolve_function_response' Volcano::Generated.autoload :ResourceReference, 'volcano-generated/models/resource_reference' +Volcano::Generated.autoload :SandboxAccess, 'volcano-generated/models/sandbox_access' +Volcano::Generated.autoload :SandboxAccessRequest, 'volcano-generated/models/sandbox_access_request' +Volcano::Generated.autoload :SandboxCapacity, 'volcano-generated/models/sandbox_capacity' +Volcano::Generated.autoload :SandboxCapacityList, 'volcano-generated/models/sandbox_capacity_list' +Volcano::Generated.autoload :SandboxCommandRequest, 'volcano-generated/models/sandbox_command_request' +Volcano::Generated.autoload :SandboxCommandResult, 'volcano-generated/models/sandbox_command_result' +Volcano::Generated.autoload :SandboxDeployment, 'volcano-generated/models/sandbox_deployment' +Volcano::Generated.autoload :SandboxDeploymentPage, 'volcano-generated/models/sandbox_deployment_page' +Volcano::Generated.autoload :SandboxExecutionRequest, 'volcano-generated/models/sandbox_execution_request' +Volcano::Generated.autoload :SandboxExecutionResult, 'volcano-generated/models/sandbox_execution_result' +Volcano::Generated.autoload :SandboxFileReadRequest, 'volcano-generated/models/sandbox_file_read_request' +Volcano::Generated.autoload :SandboxFileResult, 'volcano-generated/models/sandbox_file_result' +Volcano::Generated.autoload :SandboxFileWriteRequest, 'volcano-generated/models/sandbox_file_write_request' +Volcano::Generated.autoload :SandboxPagination, 'volcano-generated/models/sandbox_pagination' +Volcano::Generated.autoload :SandboxPreset, 'volcano-generated/models/sandbox_preset' +Volcano::Generated.autoload :SandboxPresetList, 'volcano-generated/models/sandbox_preset_list' +Volcano::Generated.autoload :SandboxSession, 'volcano-generated/models/sandbox_session' +Volcano::Generated.autoload :SandboxSessionPage, 'volcano-generated/models/sandbox_session_page' +Volcano::Generated.autoload :SandboxSubjectGrantRequest, 'volcano-generated/models/sandbox_subject_grant_request' +Volcano::Generated.autoload :SandboxTemplate, 'volcano-generated/models/sandbox_template' +Volcano::Generated.autoload :SandboxTemplatePage, 'volcano-generated/models/sandbox_template_page' Volcano::Generated.autoload :ScheduleRequest, 'volcano-generated/models/schedule_request' Volcano::Generated.autoload :ServiceKey, 'volcano-generated/models/service_key' Volcano::Generated.autoload :SetProjectGitProductionBranchRequest, 'volcano-generated/models/set_project_git_production_branch_request' @@ -360,6 +401,7 @@ Volcano::Generated.autoload :UpdateProjectGitDeploySettingsRequest, 'volcano-generated/models/update_project_git_deploy_settings_request' Volcano::Generated.autoload :UpdateProjectRequest, 'volcano-generated/models/update_project_request' Volcano::Generated.autoload :UpdateRealtimeConfigRequest, 'volcano-generated/models/update_realtime_config_request' +Volcano::Generated.autoload :UpdateSandboxTemplateRequest, 'volcano-generated/models/update_sandbox_template_request' Volcano::Generated.autoload :UpdateStorageBucketRequest, 'volcano-generated/models/update_storage_bucket_request' Volcano::Generated.autoload :UpdateVariableRequest, 'volcano-generated/models/update_variable_request' Volcano::Generated.autoload :UploadSessionPart, 'volcano-generated/models/upload_session_part' @@ -385,9 +427,11 @@ Volcano::Generated.autoload :LogsApi, 'volcano-generated/api/logs_api' Volcano::Generated.autoload :OAuthAuthenticationApi, 'volcano-generated/api/o_auth_authentication_api' Volcano::Generated.autoload :OAuthConfigurationApi, 'volcano-generated/api/o_auth_configuration_api' +Volcano::Generated.autoload :ProjectAccessTokensApi, 'volcano-generated/api/project_access_tokens_api' Volcano::Generated.autoload :ProjectImportsApi, 'volcano-generated/api/project_imports_api' Volcano::Generated.autoload :ProjectsApi, 'volcano-generated/api/projects_api' Volcano::Generated.autoload :RealtimeApi, 'volcano-generated/api/realtime_api' +Volcano::Generated.autoload :SandboxesApi, 'volcano-generated/api/sandboxes_api' Volcano::Generated.autoload :ServiceKeysApi, 'volcano-generated/api/service_keys_api' Volcano::Generated.autoload :StorageAdminApi, 'volcano-generated/api/storage_admin_api' Volcano::Generated.autoload :StorageBucketsApi, 'volcano-generated/api/storage_buckets_api' diff --git a/lib/volcano/generated/lib/volcano-generated/api/anon_keys_api.rb b/lib/volcano/generated/lib/volcano-generated/api/anon_keys_api.rb index b8e427fa..4663b2fa 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/anon_keys_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/anon_keys_api.rb @@ -72,7 +72,7 @@ def create_anon_key_with_http_info(id, create_anon_key_request, opts = {}) return_type = opts[:debug_return_type] || 'AnonKey' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AnonKeysApi.create_anon_key", @@ -141,7 +141,7 @@ def get_anon_key_with_http_info(id, key_id, opts = {}) return_type = opts[:debug_return_type] || 'AnonKey' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AnonKeysApi.get_anon_key", @@ -242,7 +242,7 @@ def list_anon_keys_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ListAnonKeys200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AnonKeysApi.list_anon_keys", @@ -311,7 +311,7 @@ def regenerate_anon_key_with_http_info(id, key_id, opts = {}) return_type = opts[:debug_return_type] || 'AnonKey' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AnonKeysApi.regenerate_anon_key", @@ -380,7 +380,7 @@ def revoke_anon_key_with_http_info(id, key_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AnonKeysApi.revoke_anon_key", @@ -449,7 +449,7 @@ def set_default_anon_key_with_http_info(id, key_id, opts = {}) return_type = opts[:debug_return_type] || 'AnonKey' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AnonKeysApi.set_default_anon_key", diff --git a/lib/volcano/generated/lib/volcano-generated/api/auth_admin_api.rb b/lib/volcano/generated/lib/volcano-generated/api/auth_admin_api.rb index b8dee32d..449606f4 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/auth_admin_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/auth_admin_api.rb @@ -76,7 +76,7 @@ def ban_auth_user_with_http_info(id, user_id, opts = {}) return_type = opts[:debug_return_type] || 'BanUserResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.ban_auth_user", @@ -143,7 +143,7 @@ def delete_all_user_sessions_with_http_info(id, user_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.delete_all_user_sessions", @@ -210,7 +210,7 @@ def delete_auth_user_with_http_info(id, user_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.delete_auth_user", @@ -283,7 +283,7 @@ def delete_user_session_with_http_info(id, user_id, session_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.delete_user_session", @@ -355,7 +355,7 @@ def get_auth_insights_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'AuthInsightsResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.get_auth_insights", @@ -422,7 +422,7 @@ def get_auth_user_with_http_info(id, user_id, opts = {}) return_type = opts[:debug_return_type] || 'AuthUser' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.get_auth_user", @@ -530,7 +530,7 @@ def list_auth_users_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedAuthUsers' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.list_auth_users", @@ -648,7 +648,7 @@ def list_user_sessions_with_http_info(id, user_id, opts = {}) return_type = opts[:debug_return_type] || 'AuthGetMySessions200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.list_user_sessions", @@ -717,7 +717,7 @@ def unban_auth_user_with_http_info(id, user_id, opts = {}) return_type = opts[:debug_return_type] || 'UnbanUserResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthAdminApi.unban_auth_user", diff --git a/lib/volcano/generated/lib/volcano-generated/api/auth_configuration_api.rb b/lib/volcano/generated/lib/volcano-generated/api/auth_configuration_api.rb index 8e72f1d7..c352f2c0 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/auth_configuration_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/auth_configuration_api.rb @@ -68,7 +68,7 @@ def configure_auth_methods_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.configure_auth_methods", @@ -142,7 +142,7 @@ def create_email_template_with_http_info(id, create_email_template_request, opts return_type = opts[:debug_return_type] || 'EmailTemplate' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.create_email_template", @@ -209,7 +209,7 @@ def delete_auth_page_layout_with_http_info(id, page_type, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.delete_auth_page_layout", @@ -270,7 +270,7 @@ def delete_auth_page_theme_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.delete_auth_page_theme", @@ -344,7 +344,7 @@ def delete_email_template_with_http_info(id, type, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.delete_email_template", @@ -405,7 +405,7 @@ def get_auth_config_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'AuthConfig' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.get_auth_config", @@ -474,7 +474,7 @@ def get_auth_hosted_page_with_http_info(id, page_type, opts = {}) return_type = opts[:debug_return_type] || 'AuthHostedPageResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.get_auth_hosted_page", @@ -537,7 +537,7 @@ def get_auth_methods_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'GetAuthMethods200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.get_auth_methods", @@ -598,7 +598,7 @@ def get_auth_page_appearance_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'AuthPageAppearanceResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.get_auth_page_appearance", @@ -793,7 +793,7 @@ def get_email_template_with_http_info(id, type, opts = {}) return_type = opts[:debug_return_type] || 'EmailTemplate' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.get_email_template", @@ -1007,7 +1007,7 @@ def list_email_templates_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ListEmailTemplates200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.list_email_templates", @@ -1085,7 +1085,7 @@ def preview_auth_page_with_http_info(id, page_type, preview_auth_page_request, o return_type = opts[:debug_return_type] || 'PreviewAuthPageResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.preview_auth_page", @@ -1391,7 +1391,7 @@ def test_email_config_with_http_info(id, test_email_request, opts = {}) return_type = opts[:debug_return_type] || 'TestEmailResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.test_email_config", @@ -1461,7 +1461,7 @@ def update_auth_config_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'AuthConfig' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.update_auth_config", @@ -1541,7 +1541,7 @@ def update_auth_hosted_page_with_http_info(id, page_type, update_auth_hosted_pag return_type = opts[:debug_return_type] || 'AuthHostedPageResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.update_auth_hosted_page", @@ -1619,7 +1619,7 @@ def update_auth_page_layout_with_http_info(id, page_type, update_auth_page_layou return_type = opts[:debug_return_type] || 'UpdateAuthPageLayoutRequest' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.update_auth_page_layout", @@ -1691,7 +1691,7 @@ def update_auth_page_theme_with_http_info(id, update_auth_page_theme_request, op return_type = opts[:debug_return_type] || 'UpdateAuthPageThemeRequest' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.update_auth_page_theme", @@ -1776,7 +1776,7 @@ def update_email_template_with_http_info(id, type, update_email_template_request return_type = opts[:debug_return_type] || 'EmailTemplate' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"AuthConfigurationApi.update_email_template", diff --git a/lib/volcano/generated/lib/volcano-generated/api/authentication_api.rb b/lib/volcano/generated/lib/volcano-generated/api/authentication_api.rb index bf4d6123..756086d4 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/authentication_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/authentication_api.rb @@ -1194,7 +1194,7 @@ def auth_reset_password_with_http_info(auth_reset_password_request, opts = {}) end # Sign in an auth user - # Authenticate with email and password. Requires an anon key. Set `session_mode` to `cookie` to request HttpOnly refresh-token storage. Cookie mode is honored only for an exact, credentialed CORS origin on the same schemeful site as this API. Otherwise the response retains the refresh token in its body. + # Authenticate with email and password. Requires an anon key. Set `session_mode` to `cookie` to request HttpOnly refresh-token storage. Cookie mode is honored only for an exact, credentialed CORS origin on the same schemeful site as this API. Otherwise the response retains the refresh token in its body. A frontend on its default Volcano URL is cross-site with this API and so always gets the body token. # @param auth_signin_request [AuthSigninRequest] # @param [Hash] opts the optional parameters # @return [AuthTokenResponse] @@ -1204,7 +1204,7 @@ def auth_signin(auth_signin_request, opts = {}) end # Sign in an auth user - # Authenticate with email and password. Requires an anon key. Set `session_mode` to `cookie` to request HttpOnly refresh-token storage. Cookie mode is honored only for an exact, credentialed CORS origin on the same schemeful site as this API. Otherwise the response retains the refresh token in its body. + # Authenticate with email and password. Requires an anon key. Set `session_mode` to `cookie` to request HttpOnly refresh-token storage. Cookie mode is honored only for an exact, credentialed CORS origin on the same schemeful site as this API. Otherwise the response retains the refresh token in its body. A frontend on its default Volcano URL is cross-site with this API and so always gets the body token. # @param auth_signin_request [AuthSigninRequest] # @param [Hash] opts the optional parameters # @return [Array<(AuthTokenResponse, Integer, Hash)>] AuthTokenResponse data, response status code and response headers diff --git a/lib/volcano/generated/lib/volcano-generated/api/database_backups_api.rb b/lib/volcano/generated/lib/volcano-generated/api/database_backups_api.rb index b66ce73c..59351a1e 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/database_backups_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/database_backups_api.rb @@ -89,7 +89,7 @@ def create_database_backup_with_http_info(id, database_name, create_database_bac return_type = opts[:debug_return_type] || 'DatabaseBackup' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.create_database_backup", @@ -178,7 +178,7 @@ def create_database_restore_with_http_info(id, database_name, create_database_re return_type = opts[:debug_return_type] || 'DatabaseRestore' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.create_database_restore", @@ -270,7 +270,7 @@ def delete_database_backup_with_http_info(id, database_name, backup_name, opts = return_type = opts[:debug_return_type] || 'DeleteDatabaseBackup200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.delete_database_backup", @@ -362,7 +362,7 @@ def get_database_backup_with_http_info(id, database_name, backup_name, opts = {} return_type = opts[:debug_return_type] || 'DatabaseBackup' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.get_database_backup", @@ -440,7 +440,7 @@ def get_database_backup_schedule_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'DatabaseBackupSchedule' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.get_database_backup_schedule", @@ -524,7 +524,7 @@ def get_database_restore_with_http_info(id, database_name, restore_id, opts = {} return_type = opts[:debug_return_type] || 'DatabaseRestore' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.get_database_restore", @@ -602,7 +602,7 @@ def list_database_backups_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'DatabaseBackupList' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.list_database_backups", @@ -680,7 +680,7 @@ def list_database_restores_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'DatabaseRestoreList' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.list_database_restores", @@ -769,7 +769,7 @@ def update_database_backup_schedule_with_http_info(id, database_name, database_b return_type = opts[:debug_return_type] || 'DatabaseBackupSchedule' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBackupsApi.update_database_backup_schedule", diff --git a/lib/volcano/generated/lib/volcano-generated/api/database_branches_api.rb b/lib/volcano/generated/lib/volcano-generated/api/database_branches_api.rb index a4b740da..bf0584ed 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/database_branches_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/database_branches_api.rb @@ -89,7 +89,7 @@ def create_database_branch_with_http_info(id, database_name, create_database_bra return_type = opts[:debug_return_type] || 'DatabaseBranch' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBranchesApi.create_database_branch", @@ -182,7 +182,7 @@ def delete_database_branch_with_http_info(id, database_name, branch_name, opts = return_type = opts[:debug_return_type] || 'DeleteDatabaseBranch202Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBranchesApi.delete_database_branch", @@ -275,7 +275,7 @@ def get_database_branch_with_http_info(id, database_name, branch_name, opts = {} return_type = opts[:debug_return_type] || 'DatabaseBranch' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBranchesApi.get_database_branch", @@ -353,7 +353,7 @@ def list_database_branches_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'DatabaseBranchList' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBranchesApi.list_database_branches", @@ -446,7 +446,7 @@ def reset_database_branch_with_http_info(id, database_name, branch_name, opts = return_type = opts[:debug_return_type] || 'DatabaseBranch' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBranchesApi.reset_database_branch", @@ -539,7 +539,7 @@ def reset_database_branch_password_with_http_info(id, database_name, branch_name return_type = opts[:debug_return_type] || 'DatabaseBranch' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBranchesApi.reset_database_branch_password", @@ -643,7 +643,7 @@ def update_database_branch_with_http_info(id, database_name, branch_name, update return_type = opts[:debug_return_type] || 'DatabaseBranch' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabaseBranchesApi.update_database_branch", diff --git a/lib/volcano/generated/lib/volcano-generated/api/databases_api.rb b/lib/volcano/generated/lib/volcano-generated/api/databases_api.rb index 1b2a876d..d3399419 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/databases_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/databases_api.rb @@ -74,7 +74,7 @@ def create_database_with_http_info(id, create_database_request, opts = {}) return_type = opts[:debug_return_type] || 'Database' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabasesApi.create_database", @@ -152,7 +152,7 @@ def delete_database_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'DeleteDatabase202Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabasesApi.delete_database", @@ -228,7 +228,7 @@ def get_database_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'Database' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabasesApi.get_database", @@ -319,7 +319,7 @@ def get_database_stats_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'DatabaseStats' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabasesApi.get_database_stats", @@ -408,7 +408,7 @@ def get_project_database_queries_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'DatabaseQueryPerformanceResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabasesApi.get_project_database_queries", @@ -573,7 +573,7 @@ def list_databases_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedDatabases' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabasesApi.list_databases", @@ -708,7 +708,7 @@ def reset_database_password_with_http_info(id, database_name, opts = {}) return_type = opts[:debug_return_type] || 'ResetDatabasePassword200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabasesApi.reset_database_password", @@ -797,7 +797,7 @@ def update_database_type_with_http_info(id, database_name, update_database_type_ return_type = opts[:debug_return_type] || 'Database' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"DatabasesApi.update_database_type", diff --git a/lib/volcano/generated/lib/volcano-generated/api/durable_functions_api.rb b/lib/volcano/generated/lib/volcano-generated/api/durable_functions_api.rb index aa672edb..2d8b25dc 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/durable_functions_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/durable_functions_api.rb @@ -219,7 +219,7 @@ def create_durable_function_scheduler_with_http_info(id, function_id, create_fun end # Delete a durable function - # Accepted for asynchronous teardown; the work continues after the response. The function's executions go with it: history stops being readable whatever `retention_days` had left, and the executions still running stop counting against the project's concurrency cap. Stop an execution first if you need it to end before the function does. + # Accepted for asynchronous teardown; the work continues after the response. The function's executions go with it: executions still in flight are stopped, and history stops being readable whatever `retention_days` had left. Stopping is asynchronous at the platform, and it does not interrupt a step already running -- that step runs to its next checkpoint. So a delete ends an execution rather than halting it mid-step; stop the execution yourself first if you need to observe it ending. # @param id [String] Project ID # @param function_id [String] Durable function ID, or its name within the project # @param [Hash] opts the optional parameters @@ -230,7 +230,7 @@ def delete_durable_function(id, function_id, opts = {}) end # Delete a durable function - # Accepted for asynchronous teardown; the work continues after the response. The function's executions go with it: history stops being readable whatever `retention_days` had left, and the executions still running stop counting against the project's concurrency cap. Stop an execution first if you need it to end before the function does. + # Accepted for asynchronous teardown; the work continues after the response. The function's executions go with it: executions still in flight are stopped, and history stops being readable whatever `retention_days` had left. Stopping is asynchronous at the platform, and it does not interrupt a step already running -- that step runs to its next checkpoint. So a delete ends an execution rather than halting it mid-step; stop the execution yourself first if you need to observe it ending. # @param id [String] Project ID # @param function_id [String] Durable function ID, or its name within the project # @param [Hash] opts the optional parameters diff --git a/lib/volcano/generated/lib/volcano-generated/api/frontends_api.rb b/lib/volcano/generated/lib/volcano-generated/api/frontends_api.rb index f696de4c..99e34720 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/frontends_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/frontends_api.rb @@ -20,13 +20,15 @@ def initialize(api_client = ApiClient.default) @api_client = api_client end # Create a new frontend deployment - # Creates and deploys a frontend for the project. If a frontend with the same name already exists in the project, this operation updates that frontend using the uploaded archive and starts a new deployment. A deployment that starts immediately returns `status: provisioning`, then transitions to `active`, `degraded`, or `failed`. If another deployment is running, the response preserves the frontend's current status and exposes the queued deployment through `pending_deployment_id`. Existing frontend traffic continues to use an available runtime while the new deployment builds and provisions. Each deployment publishes its own static assets before the runtimes switch to its build, and the live build's assets keep serving until the new deployment is live, so a page loaded mid-deployment resolves its assets whichever build served it. A failed redeploy puts the runtimes back on the build they were running, leaves the frontend `active` on the previous deployment, and records the attempted deployment as failed. `degraded` means the runtime remains available but edge synchronization requires recovery; Volcano retries the edge step without rebuilding. Only one deployment may run for a given frontend, while independent frontends and projects can deploy concurrently. For monorepos, provide `app_root` as a relative path from the uploaded archive root to the Next.js app that should be built. Omit it for single-app archives. Supported frontend environments are Next.js 15.x and 16.x with Node.js 22.x or 24.x. The Node.js runtime is inferred from `package.json` `engines.node`; if omitted, Volcano uses Node.js 22.x. The selected Node.js family must also satisfy the installed Next.js package's `engines.node` constraint. Volcano tests Next 15.5.25 (`^18.18.0 || ^19.8.0 || >=20.0.0`) and Next 16.3.5 (`>=20.9.0`). Source archive size is enforced by the API with `SOURCE_ARCHIVE_SIZE_LIMIT_MB`; the CLI does not apply its own source archive size limit. After the final container images are built, the publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` before pushing. This operation is limited by plan-based frontend deployment quotas (`FREE_FRONTEND_DEPLOYMENTS`, `PRO_FRONTEND_DEPLOYMENTS`). Each project can contain up to 10,000 frontends regardless of plan. + # Creates and deploys a frontend for the project. If a frontend with the same name already exists in the project, this operation updates that frontend using the uploaded archive and starts a new deployment. A deployment that starts immediately returns `status: provisioning`, then transitions to `active`, `degraded`, or `failed`. If another deployment is running, the response preserves the frontend's current status and exposes the queued deployment through `pending_deployment_id`. Existing frontend traffic continues to use an available runtime while the new deployment builds and provisions. Each deployment publishes its own static assets before the runtimes switch to its build, and the live build's assets keep serving until the new deployment is live, so a page loaded mid-deployment resolves its assets whichever build served it. A failed redeploy puts the runtimes back on the build they were running, leaves the frontend `active` on the previous deployment, and records the attempted deployment as failed. `degraded` means the runtime remains available but edge synchronization requires recovery; Volcano retries the edge step without rebuilding. Only one deployment may run for a given frontend, while independent frontends and projects can deploy concurrently. For monorepos, provide `app_root` as a relative path from the uploaded archive root to the Next.js app that should be built. Omit it for single-app archives. Supported frontend environments are Next.js 15.x and 16.x with Node.js 22.x or 24.x. The Node.js runtime is inferred from `package.json` `engines.node`; if omitted, Volcano uses Node.js 22.x. The selected Node.js family must also satisfy the installed Next.js package's `engines.node` constraint. Volcano tests Next 15.5.26 (`^18.18.0 || ^19.8.0 || >=20.0.0`) and Next 16.3.6 (`>=20.9.0`). Source archive size is enforced by the API with `SOURCE_ARCHIVE_SIZE_LIMIT_MB`; the CLI does not apply its own source archive size limit. After the final container images are built, the publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` before pushing. This operation is limited by plan-based frontend deployment quotas (`FREE_FRONTEND_DEPLOYMENTS`, `PRO_FRONTEND_DEPLOYMENTS`). Each project can contain up to 10,000 frontends regardless of plan. # @param id [String] Project ID # @param name [String] DNS-safe frontend name # @param archive [File] ZIP or tar.gz archive of the frontend project directory or monorepo workspace root. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz archive. # @param [Hash] opts the optional parameters # @option opts [String] :framework Next.js frontend. Supported Next.js majors are 15.x and 16.x. (default to 'nextjs') # @option opts [String] :app_root Optional relative POSIX path from the uploaded archive root to the Next.js app to build, for example `apps/web`. + # @option opts [String] :variable_scope Variable selection for this deployment. New frontends default to `scoped`; omitting this field for an existing frontend preserves its current selection. + # @option opts [Array] :variables Project variable names selected when `variable_scope` is `scoped`. Submit each name as a repeated multipart field. # @return [Frontend] def create_frontend(id, name, archive, opts = {}) data, _status_code, _headers = create_frontend_with_http_info(id, name, archive, opts) @@ -34,13 +36,15 @@ def create_frontend(id, name, archive, opts = {}) end # Create a new frontend deployment - # Creates and deploys a frontend for the project. If a frontend with the same name already exists in the project, this operation updates that frontend using the uploaded archive and starts a new deployment. A deployment that starts immediately returns `status: provisioning`, then transitions to `active`, `degraded`, or `failed`. If another deployment is running, the response preserves the frontend's current status and exposes the queued deployment through `pending_deployment_id`. Existing frontend traffic continues to use an available runtime while the new deployment builds and provisions. Each deployment publishes its own static assets before the runtimes switch to its build, and the live build's assets keep serving until the new deployment is live, so a page loaded mid-deployment resolves its assets whichever build served it. A failed redeploy puts the runtimes back on the build they were running, leaves the frontend `active` on the previous deployment, and records the attempted deployment as failed. `degraded` means the runtime remains available but edge synchronization requires recovery; Volcano retries the edge step without rebuilding. Only one deployment may run for a given frontend, while independent frontends and projects can deploy concurrently. For monorepos, provide `app_root` as a relative path from the uploaded archive root to the Next.js app that should be built. Omit it for single-app archives. Supported frontend environments are Next.js 15.x and 16.x with Node.js 22.x or 24.x. The Node.js runtime is inferred from `package.json` `engines.node`; if omitted, Volcano uses Node.js 22.x. The selected Node.js family must also satisfy the installed Next.js package's `engines.node` constraint. Volcano tests Next 15.5.25 (`^18.18.0 || ^19.8.0 || >=20.0.0`) and Next 16.3.5 (`>=20.9.0`). Source archive size is enforced by the API with `SOURCE_ARCHIVE_SIZE_LIMIT_MB`; the CLI does not apply its own source archive size limit. After the final container images are built, the publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` before pushing. This operation is limited by plan-based frontend deployment quotas (`FREE_FRONTEND_DEPLOYMENTS`, `PRO_FRONTEND_DEPLOYMENTS`). Each project can contain up to 10,000 frontends regardless of plan. + # Creates and deploys a frontend for the project. If a frontend with the same name already exists in the project, this operation updates that frontend using the uploaded archive and starts a new deployment. A deployment that starts immediately returns `status: provisioning`, then transitions to `active`, `degraded`, or `failed`. If another deployment is running, the response preserves the frontend's current status and exposes the queued deployment through `pending_deployment_id`. Existing frontend traffic continues to use an available runtime while the new deployment builds and provisions. Each deployment publishes its own static assets before the runtimes switch to its build, and the live build's assets keep serving until the new deployment is live, so a page loaded mid-deployment resolves its assets whichever build served it. A failed redeploy puts the runtimes back on the build they were running, leaves the frontend `active` on the previous deployment, and records the attempted deployment as failed. `degraded` means the runtime remains available but edge synchronization requires recovery; Volcano retries the edge step without rebuilding. Only one deployment may run for a given frontend, while independent frontends and projects can deploy concurrently. For monorepos, provide `app_root` as a relative path from the uploaded archive root to the Next.js app that should be built. Omit it for single-app archives. Supported frontend environments are Next.js 15.x and 16.x with Node.js 22.x or 24.x. The Node.js runtime is inferred from `package.json` `engines.node`; if omitted, Volcano uses Node.js 22.x. The selected Node.js family must also satisfy the installed Next.js package's `engines.node` constraint. Volcano tests Next 15.5.26 (`^18.18.0 || ^19.8.0 || >=20.0.0`) and Next 16.3.6 (`>=20.9.0`). Source archive size is enforced by the API with `SOURCE_ARCHIVE_SIZE_LIMIT_MB`; the CLI does not apply its own source archive size limit. After the final container images are built, the publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` before pushing. This operation is limited by plan-based frontend deployment quotas (`FREE_FRONTEND_DEPLOYMENTS`, `PRO_FRONTEND_DEPLOYMENTS`). Each project can contain up to 10,000 frontends regardless of plan. # @param id [String] Project ID # @param name [String] DNS-safe frontend name # @param archive [File] ZIP or tar.gz archive of the frontend project directory or monorepo workspace root. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz archive. # @param [Hash] opts the optional parameters # @option opts [String] :framework Next.js frontend. Supported Next.js majors are 15.x and 16.x. (default to 'nextjs') # @option opts [String] :app_root Optional relative POSIX path from the uploaded archive root to the Next.js app to build, for example `apps/web`. + # @option opts [String] :variable_scope Variable selection for this deployment. New frontends default to `scoped`; omitting this field for an existing frontend preserves its current selection. + # @option opts [Array] :variables Project variable names selected when `variable_scope` is `scoped`. Submit each name as a repeated multipart field. # @return [Array<(Frontend, Integer, Hash)>] Frontend data, response status code and response headers def create_frontend_with_http_info(id, name, archive, opts = {}) if @api_client.config.debugging @@ -75,6 +79,10 @@ def create_frontend_with_http_info(id, name, archive, opts = {}) fail ArgumentError, 'invalid value for "opts[:"app_root"]" when calling FrontendsApi.create_frontend, the character length must be smaller than or equal to 1024.' end + allowable_values = ["all", "scoped"] + if @api_client.config.client_side_validation && opts[:'variable_scope'] && !allowable_values.include?(opts[:'variable_scope']) + fail ArgumentError, "invalid value for \"variable_scope\", must be one of #{allowable_values}" + end # resource path local_var_path = '/projects/{id}/frontends'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) @@ -97,6 +105,8 @@ def create_frontend_with_http_info(id, name, archive, opts = {}) form_params['archive'] = archive form_params['framework'] = opts[:'framework'] if !opts[:'framework'].nil? form_params['app_root'] = opts[:'app_root'] if !opts[:'app_root'].nil? + form_params['variable_scope'] = opts[:'variable_scope'] if !opts[:'variable_scope'].nil? + form_params['variables'] = @api_client.build_collection_param(opts[:'variables'], :csv) if !opts[:'variables'].nil? # http body (model) post_body = opts[:debug_body] @@ -105,7 +115,7 @@ def create_frontend_with_http_info(id, name, archive, opts = {}) return_type = opts[:debug_return_type] || 'Frontend' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.create_frontend", @@ -185,7 +195,7 @@ def create_frontend_custom_domain_with_http_info(id, frontend_id, create_fronten return_type = opts[:debug_return_type] || 'FrontendCustomDomainResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.create_frontend_custom_domain", @@ -204,6 +214,86 @@ def create_frontend_custom_domain_with_http_info(id, frontend_id, create_fronten return data, status_code, headers end + # Route a Frontend path to an HTTP Function + # The Frontend and Function must belong to this Project. The Function may be private but must use HTTP invocation mode. The route applies to every hostname that resolves to the Frontend, including generated, custom-domain, preview, and local hostnames. + # @param id [String] Project ID + # @param frontend_id [String] Frontend ID + # @param create_frontend_function_route_request [CreateFrontendFunctionRouteRequest] + # @param [Hash] opts the optional parameters + # @return [FrontendFunctionRoute] + def create_frontend_function_route(id, frontend_id, create_frontend_function_route_request, opts = {}) + data, _status_code, _headers = create_frontend_function_route_with_http_info(id, frontend_id, create_frontend_function_route_request, opts) + data + end + + # Route a Frontend path to an HTTP Function + # The Frontend and Function must belong to this Project. The Function may be private but must use HTTP invocation mode. The route applies to every hostname that resolves to the Frontend, including generated, custom-domain, preview, and local hostnames. + # @param id [String] Project ID + # @param frontend_id [String] Frontend ID + # @param create_frontend_function_route_request [CreateFrontendFunctionRouteRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(FrontendFunctionRoute, Integer, Hash)>] FrontendFunctionRoute data, response status code and response headers + def create_frontend_function_route_with_http_info(id, frontend_id, create_frontend_function_route_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: FrontendsApi.create_frontend_function_route ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling FrontendsApi.create_frontend_function_route" + end + # verify the required parameter 'frontend_id' is set + if @api_client.config.client_side_validation && frontend_id.nil? + fail ArgumentError, "Missing the required parameter 'frontend_id' when calling FrontendsApi.create_frontend_function_route" + end + # verify the required parameter 'create_frontend_function_route_request' is set + if @api_client.config.client_side_validation && create_frontend_function_route_request.nil? + fail ArgumentError, "Missing the required parameter 'create_frontend_function_route_request' when calling FrontendsApi.create_frontend_function_route" + end + # resource path + local_var_path = '/projects/{id}/frontends/{frontendId}/function-routes'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'frontendId' + '}', CGI.escape(frontend_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(create_frontend_function_route_request) + + # return_type + return_type = opts[:debug_return_type] || 'FrontendFunctionRoute' + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] + + new_options = opts.merge( + :operation => :"FrontendsApi.create_frontend_function_route", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: FrontendsApi#create_frontend_function_route\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + # Delete a frontend # Schedules asynchronous frontend deletion. If another deployment is running, the frontend preserves its current status and exposes the queued deletion through `pending_deployment_id`. Its status changes to `deleting` when cleanup starts. After cleanup, it returns 404 and no longer appears in frontend lists. # @param id [String] Project ID @@ -254,7 +344,7 @@ def delete_frontend_with_http_info(id, frontend_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.delete_frontend", @@ -321,7 +411,7 @@ def delete_frontend_custom_domain_with_http_info(id, frontend_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.delete_frontend_custom_domain", @@ -340,6 +430,79 @@ def delete_frontend_custom_domain_with_http_info(id, frontend_id, opts = {}) return data, status_code, headers end + # Delete a Frontend Function route + # @param id [String] Project ID + # @param frontend_id [String] Frontend ID + # @param route_id [String] Frontend Function route ID + # @param [Hash] opts the optional parameters + # @return [nil] + def delete_frontend_function_route(id, frontend_id, route_id, opts = {}) + delete_frontend_function_route_with_http_info(id, frontend_id, route_id, opts) + nil + end + + # Delete a Frontend Function route + # @param id [String] Project ID + # @param frontend_id [String] Frontend ID + # @param route_id [String] Frontend Function route ID + # @param [Hash] opts the optional parameters + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def delete_frontend_function_route_with_http_info(id, frontend_id, route_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: FrontendsApi.delete_frontend_function_route ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling FrontendsApi.delete_frontend_function_route" + end + # verify the required parameter 'frontend_id' is set + if @api_client.config.client_side_validation && frontend_id.nil? + fail ArgumentError, "Missing the required parameter 'frontend_id' when calling FrontendsApi.delete_frontend_function_route" + end + # verify the required parameter 'route_id' is set + if @api_client.config.client_side_validation && route_id.nil? + fail ArgumentError, "Missing the required parameter 'route_id' when calling FrontendsApi.delete_frontend_function_route" + end + # resource path + local_var_path = '/projects/{id}/frontends/{frontendId}/function-routes/{routeId}'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'frontendId' + '}', CGI.escape(frontend_id.to_s)).sub('{' + 'routeId' + '}', CGI.escape(route_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] + + new_options = opts.merge( + :operation => :"FrontendsApi.delete_frontend_function_route", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:DELETE, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: FrontendsApi#delete_frontend_function_route\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + # Get frontend details # @param id [String] Project ID # @param frontend_id [String] Frontend ID @@ -388,7 +551,7 @@ def get_frontend_with_http_info(id, frontend_id, opts = {}) return_type = opts[:debug_return_type] || 'Frontend' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.get_frontend", @@ -455,7 +618,7 @@ def get_frontend_custom_domain_with_http_info(id, frontend_id, opts = {}) return_type = opts[:debug_return_type] || 'FrontendCustomDomainResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.get_frontend_custom_domain", @@ -535,7 +698,7 @@ def get_frontend_usage_history_with_http_info(id, frontend_id, opts = {}) return_type = opts[:debug_return_type] || 'FrontendUsageHistoryResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.get_frontend_usage_history", @@ -620,7 +783,7 @@ def list_frontend_deployments_with_http_info(id, frontend_id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedFrontendDeployments' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.list_frontend_deployments", @@ -639,6 +802,73 @@ def list_frontend_deployments_with_http_info(id, frontend_id, opts = {}) return data, status_code, headers end + # List a Frontend's Function routes + # @param id [String] Project ID + # @param frontend_id [String] Frontend ID + # @param [Hash] opts the optional parameters + # @return [FrontendFunctionRouteList] + def list_frontend_function_routes(id, frontend_id, opts = {}) + data, _status_code, _headers = list_frontend_function_routes_with_http_info(id, frontend_id, opts) + data + end + + # List a Frontend's Function routes + # @param id [String] Project ID + # @param frontend_id [String] Frontend ID + # @param [Hash] opts the optional parameters + # @return [Array<(FrontendFunctionRouteList, Integer, Hash)>] FrontendFunctionRouteList data, response status code and response headers + def list_frontend_function_routes_with_http_info(id, frontend_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: FrontendsApi.list_frontend_function_routes ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling FrontendsApi.list_frontend_function_routes" + end + # verify the required parameter 'frontend_id' is set + if @api_client.config.client_side_validation && frontend_id.nil? + fail ArgumentError, "Missing the required parameter 'frontend_id' when calling FrontendsApi.list_frontend_function_routes" + end + # resource path + local_var_path = '/projects/{id}/frontends/{frontendId}/function-routes'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'frontendId' + '}', CGI.escape(frontend_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'FrontendFunctionRouteList' + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] + + new_options = opts.merge( + :operation => :"FrontendsApi.list_frontend_function_routes", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: FrontendsApi#list_frontend_function_routes\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + # List all frontends in a project # Supports two mutually exclusive pagination modes. Offset mode uses `page` and `limit` and returns `next` (URL). Cursor mode uses `cursor` and `limit`, supports `search` (case-insensitive name match), and returns `next_cursor`. Sending both `page` and `cursor` (or `page` and `search`) returns 400. # @param id [String] Project ID @@ -721,7 +951,7 @@ def list_frontends_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedFrontends' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.list_frontends", @@ -822,7 +1052,7 @@ def list_project_custom_domains_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedProjectCustomDomains' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.list_project_custom_domains", @@ -891,7 +1121,7 @@ def redeploy_frontend_with_http_info(id, frontend_id, opts = {}) return_type = opts[:debug_return_type] || 'Frontend' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FrontendsApi.redeploy_frontend", @@ -909,5 +1139,89 @@ def redeploy_frontend_with_http_info(id, frontend_id, opts = {}) end return data, status_code, headers end + + # Replace a Frontend Function route + # @param id [String] Project ID + # @param frontend_id [String] Frontend ID + # @param route_id [String] Frontend Function route ID + # @param create_frontend_function_route_request [CreateFrontendFunctionRouteRequest] + # @param [Hash] opts the optional parameters + # @return [FrontendFunctionRoute] + def update_frontend_function_route(id, frontend_id, route_id, create_frontend_function_route_request, opts = {}) + data, _status_code, _headers = update_frontend_function_route_with_http_info(id, frontend_id, route_id, create_frontend_function_route_request, opts) + data + end + + # Replace a Frontend Function route + # @param id [String] Project ID + # @param frontend_id [String] Frontend ID + # @param route_id [String] Frontend Function route ID + # @param create_frontend_function_route_request [CreateFrontendFunctionRouteRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(FrontendFunctionRoute, Integer, Hash)>] FrontendFunctionRoute data, response status code and response headers + def update_frontend_function_route_with_http_info(id, frontend_id, route_id, create_frontend_function_route_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: FrontendsApi.update_frontend_function_route ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling FrontendsApi.update_frontend_function_route" + end + # verify the required parameter 'frontend_id' is set + if @api_client.config.client_side_validation && frontend_id.nil? + fail ArgumentError, "Missing the required parameter 'frontend_id' when calling FrontendsApi.update_frontend_function_route" + end + # verify the required parameter 'route_id' is set + if @api_client.config.client_side_validation && route_id.nil? + fail ArgumentError, "Missing the required parameter 'route_id' when calling FrontendsApi.update_frontend_function_route" + end + # verify the required parameter 'create_frontend_function_route_request' is set + if @api_client.config.client_side_validation && create_frontend_function_route_request.nil? + fail ArgumentError, "Missing the required parameter 'create_frontend_function_route_request' when calling FrontendsApi.update_frontend_function_route" + end + # resource path + local_var_path = '/projects/{id}/frontends/{frontendId}/function-routes/{routeId}'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'frontendId' + '}', CGI.escape(frontend_id.to_s)).sub('{' + 'routeId' + '}', CGI.escape(route_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(create_frontend_function_route_request) + + # return_type + return_type = opts[:debug_return_type] || 'FrontendFunctionRoute' + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] + + new_options = opts.merge( + :operation => :"FrontendsApi.update_frontend_function_route", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:PUT, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: FrontendsApi#update_frontend_function_route\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end end end diff --git a/lib/volcano/generated/lib/volcano-generated/api/functions_api.rb b/lib/volcano/generated/lib/volcano-generated/api/functions_api.rb index 0885591f..9824fc69 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/functions_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/functions_api.rb @@ -128,7 +128,7 @@ def create_function_with_http_info(id, name, code, runtime, opts = {}) return_type = opts[:debug_return_type] || 'Function' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.create_function", @@ -208,7 +208,7 @@ def create_function_scheduler_with_http_info(id, function_id, create_function_sc return_type = opts[:debug_return_type] || 'FunctionScheduler' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.create_function_scheduler", @@ -286,7 +286,7 @@ def create_functions_batch_with_http_info(id, functions, opts = {}) return_type = opts[:debug_return_type] || 'BatchFunctionDeployResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.create_functions_batch", @@ -355,7 +355,7 @@ def delete_function_with_http_info(id, function_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.delete_function", @@ -428,7 +428,7 @@ def delete_function_scheduler_with_http_info(id, function_id, scheduler_id, opts return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.delete_function_scheduler", @@ -495,7 +495,7 @@ def get_function_with_http_info(id, function_id, opts = {}) return_type = opts[:debug_return_type] || 'Function' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.get_function", @@ -568,7 +568,7 @@ def get_function_scheduler_with_http_info(id, function_id, scheduler_id, opts = return_type = opts[:debug_return_type] || 'FunctionScheduler' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.get_function_scheduler", @@ -588,7 +588,7 @@ def get_function_scheduler_with_http_info(id, function_id, scheduler_id, opts = end # Invoke a function - # Invoke a serverless function. **With Service Key** (admin/background operations): - Use for background jobs, webhooks, cron, admin operations - Function receives payload only (no user context) - Database queries bypass RLS (admin access) **With Auth User Token** (user-facing): - Use for user-initiated actions - Function receives payload + `__volcano_auth` context: ```javascript { user_id: \"uuid\", email: \"user@example.com\", project_id: \"uuid\", role: \"authenticated\" or \"anonymous\" } ``` - Database queries enforce RLS (user-scoped data) **With Anon Key** (public function only): - Requires anon key permission: `functions.invoke` - Function must have `is_public: true` - Function receives payload only (no `__volcano_auth`) **Transport and CORS:** - This operation is the authenticated direct RPC endpoint and always uses the POST `{payload: ...}` contract, including for functions whose DNS ingress is configured in HTTP mode. - The geo-routed DNS ingress is `https://{functionId}.functions./`. - RPC-mode DNS ingress accepts POST at `/`. HTTP-mode DNS ingress accepts GET, HEAD, POST, PUT, PATCH, and DELETE at `/` and nested paths. - Direct and RPC-mode CORS preflight advertises `POST, OPTIONS`. HTTP-mode DNS preflight advertises `GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS`. - `http_auth_mode: none` applies only to public HTTP-mode DNS ingress; this direct operation always requires a Volcano credential. **Durable functions are not invocable here.** A durable function's id answers 404, whatever its visibility, because a synchronous call would run it with no execution record, no idempotency and no concurrency accounting. Start one with `POST /durable-functions/{functionId}/executions`. + # Invoke a serverless function. **With Service Key** (admin/background operations): - Use for background jobs, webhooks, cron, admin operations - Function receives payload only (no user context) - Database queries bypass RLS (admin access) **With Auth User Token** (user-facing): - Use for user-initiated actions - Function receives payload + `__volcano_auth` context: ```javascript { user_id: \"uuid\", email: \"user@example.com\", project_id: \"uuid\", role: \"authenticated\" or \"anonymous\" } ``` - Database queries enforce RLS (user-scoped data) **With Anon Key** (public function only): - Requires anon key permission: `functions.invoke` - Function must have `is_public: true` - Function receives payload only (no `__volcano_auth`) **Transport and CORS:** - This operation is the authenticated direct RPC endpoint and always uses the POST `{payload: ...}` contract, including for functions whose DNS ingress is configured in HTTP mode. - The geo-routed DNS ingress is the function's `invoke_url`. It is on a different domain from this API, so it cannot be derived from the API host. - RPC-mode DNS ingress accepts POST at `/`. HTTP-mode DNS ingress accepts GET, HEAD, POST, PUT, PATCH, and DELETE at `/` and nested paths. - Direct and RPC-mode CORS preflight advertises `POST, OPTIONS`. HTTP-mode DNS preflight advertises `GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS`. - `http_auth_mode: none` applies only to public HTTP-mode DNS ingress; this direct operation always requires a Volcano credential. **Durable functions are not invocable here.** A durable function's id answers 404, whatever its visibility, because a synchronous call would run it with no execution record, no idempotency and no concurrency accounting. Start one with `POST /durable-functions/{functionId}/executions`. # @param function_id [String] Function ID # @param function_invocation_request [FunctionInvocationRequest] # @param [Hash] opts the optional parameters @@ -599,7 +599,7 @@ def invoke_function(function_id, function_invocation_request, opts = {}) end # Invoke a function - # Invoke a serverless function. **With Service Key** (admin/background operations): - Use for background jobs, webhooks, cron, admin operations - Function receives payload only (no user context) - Database queries bypass RLS (admin access) **With Auth User Token** (user-facing): - Use for user-initiated actions - Function receives payload + `__volcano_auth` context: ```javascript { user_id: \"uuid\", email: \"user@example.com\", project_id: \"uuid\", role: \"authenticated\" or \"anonymous\" } ``` - Database queries enforce RLS (user-scoped data) **With Anon Key** (public function only): - Requires anon key permission: `functions.invoke` - Function must have `is_public: true` - Function receives payload only (no `__volcano_auth`) **Transport and CORS:** - This operation is the authenticated direct RPC endpoint and always uses the POST `{payload: ...}` contract, including for functions whose DNS ingress is configured in HTTP mode. - The geo-routed DNS ingress is `https://{functionId}.functions.<domain>/`. - RPC-mode DNS ingress accepts POST at `/`. HTTP-mode DNS ingress accepts GET, HEAD, POST, PUT, PATCH, and DELETE at `/` and nested paths. - Direct and RPC-mode CORS preflight advertises `POST, OPTIONS`. HTTP-mode DNS preflight advertises `GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS`. - `http_auth_mode: none` applies only to public HTTP-mode DNS ingress; this direct operation always requires a Volcano credential. **Durable functions are not invocable here.** A durable function's id answers 404, whatever its visibility, because a synchronous call would run it with no execution record, no idempotency and no concurrency accounting. Start one with `POST /durable-functions/{functionId}/executions`. + # Invoke a serverless function. **With Service Key** (admin/background operations): - Use for background jobs, webhooks, cron, admin operations - Function receives payload only (no user context) - Database queries bypass RLS (admin access) **With Auth User Token** (user-facing): - Use for user-initiated actions - Function receives payload + `__volcano_auth` context: ```javascript { user_id: \"uuid\", email: \"user@example.com\", project_id: \"uuid\", role: \"authenticated\" or \"anonymous\" } ``` - Database queries enforce RLS (user-scoped data) **With Anon Key** (public function only): - Requires anon key permission: `functions.invoke` - Function must have `is_public: true` - Function receives payload only (no `__volcano_auth`) **Transport and CORS:** - This operation is the authenticated direct RPC endpoint and always uses the POST `{payload: ...}` contract, including for functions whose DNS ingress is configured in HTTP mode. - The geo-routed DNS ingress is the function's `invoke_url`. It is on a different domain from this API, so it cannot be derived from the API host. - RPC-mode DNS ingress accepts POST at `/`. HTTP-mode DNS ingress accepts GET, HEAD, POST, PUT, PATCH, and DELETE at `/` and nested paths. - Direct and RPC-mode CORS preflight advertises `POST, OPTIONS`. HTTP-mode DNS preflight advertises `GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS`. - `http_auth_mode: none` applies only to public HTTP-mode DNS ingress; this direct operation always requires a Volcano credential. **Durable functions are not invocable here.** A durable function's id answers 404, whatever its visibility, because a synchronous call would run it with no execution record, no idempotency and no concurrency accounting. Start one with `POST /durable-functions/{functionId}/executions`. # @param function_id [String] Function ID # @param function_invocation_request [FunctionInvocationRequest] # @param [Hash] opts the optional parameters @@ -727,7 +727,7 @@ def list_function_deployments_with_http_info(id, function_id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedFunctionDeployments' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.list_function_deployments", @@ -908,7 +908,7 @@ def list_function_schedulers_with_http_info(id, function_id, opts = {}) return_type = opts[:debug_return_type] || 'FunctionSchedulerListResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.list_function_schedulers", @@ -1009,7 +1009,7 @@ def list_functions_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedFunctions' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.list_functions", @@ -1110,7 +1110,7 @@ def list_project_schedulers_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'FunctionSchedulerListResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.list_project_schedulers", @@ -1130,7 +1130,7 @@ def list_project_schedulers_with_http_info(id, opts = {}) end # Resolve function name for invocation - # Resolves a DNS-safe function name to its function ID within the caller's project. SDKs use this endpoint internally to invoke by function name while routing by function ID. **With Service Key**: - Allowed **With Auth User Token**: - Allowed **With Anon Key**: - Requires anon key permission: `functions.invoke` - Function must have `is_public: true` + # Resolves a DNS-safe function name to its function ID and invocation URL within the caller's project. SDKs use this endpoint internally to invoke by function name while routing by function ID. Invoke the returned `invoke_url` as-is. It does not share a domain with the API, so a host built from the API URL will not reach the function. When the deployment serves no public invocation domain, as in local development, `invoke_url` is omitted and callers invoke through `POST /functions/{functionId}/invoke`. **With Service Key**: - Allowed **With Auth User Token**: - Allowed **With Anon Key**: - Requires anon key permission: `functions.invoke` - Function must have `is_public: true` # @param name [String] DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) # @param [Hash] opts the optional parameters # @return [ResolveFunctionResponse] @@ -1140,7 +1140,7 @@ def resolve_function_for_invocation(name, opts = {}) end # Resolve function name for invocation - # Resolves a DNS-safe function name to its function ID within the caller's project. SDKs use this endpoint internally to invoke by function name while routing by function ID. **With Service Key**: - Allowed **With Auth User Token**: - Allowed **With Anon Key**: - Requires anon key permission: `functions.invoke` - Function must have `is_public: true` + # Resolves a DNS-safe function name to its function ID and invocation URL within the caller's project. SDKs use this endpoint internally to invoke by function name while routing by function ID. Invoke the returned `invoke_url` as-is. It does not share a domain with the API, so a host built from the API URL will not reach the function. When the deployment serves no public invocation domain, as in local development, `invoke_url` is omitted and callers invoke through `POST /functions/{functionId}/invoke`. **With Service Key**: - Allowed **With Auth User Token**: - Allowed **With Anon Key**: - Requires anon key permission: `functions.invoke` - Function must have `is_public: true` # @param name [String] DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) # @param [Hash] opts the optional parameters # @return [Array<(ResolveFunctionResponse, Integer, Hash)>] ResolveFunctionResponse data, response status code and response headers @@ -1261,7 +1261,7 @@ def update_function_with_http_info(id, function_id, update_function_request, opt return_type = opts[:debug_return_type] || 'Function' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.update_function", @@ -1345,7 +1345,7 @@ def update_function_scheduler_with_http_info(id, function_id, scheduler_id, upda return_type = opts[:debug_return_type] || 'FunctionScheduler' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"FunctionsApi.update_function_scheduler", diff --git a/lib/volcano/generated/lib/volcano-generated/api/git_connections_api.rb b/lib/volcano/generated/lib/volcano-generated/api/git_connections_api.rb index 96063c73..ff545303 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/git_connections_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/git_connections_api.rb @@ -63,7 +63,7 @@ def cancel_project_source_export_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.cancel_project_source_export", @@ -137,7 +137,7 @@ def connect_project_git_with_http_info(id, connect_project_git_request, opts = { return_type = opts[:debug_return_type] || 'ProjectGitConnection' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.connect_project_git", @@ -259,7 +259,7 @@ def disconnect_project_git_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.disconnect_project_git", @@ -333,7 +333,7 @@ def export_project_source_with_http_info(id, export_project_source_request, opts return_type = opts[:debug_return_type] || 'ProjectSourceExport' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.export_project_source", @@ -394,7 +394,7 @@ def get_project_git_connection_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectGitConnection' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.get_project_git_connection", @@ -455,7 +455,7 @@ def get_project_git_deploy_settings_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectGitDeploySettings' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.get_project_git_deploy_settings", @@ -518,7 +518,7 @@ def get_project_source_export_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectSourceExportState' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.get_project_source_export", @@ -849,7 +849,7 @@ def set_project_git_production_branch_with_http_info(id, set_project_git_product return_type = opts[:debug_return_type] || 'ProjectGitConnection' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.set_project_git_production_branch", @@ -990,7 +990,7 @@ def update_project_git_deploy_settings_with_http_info(id, update_project_git_dep return_type = opts[:debug_return_type] || 'ProjectGitDeploySettings' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"GitConnectionsApi.update_project_git_deploy_settings", diff --git a/lib/volcano/generated/lib/volcano-generated/api/logs_api.rb b/lib/volcano/generated/lib/volcano-generated/api/logs_api.rb index e3b54e43..c1d7faed 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/logs_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/logs_api.rb @@ -74,7 +74,7 @@ def get_project_log_activity_with_http_info(id, log_activity_request, opts = {}) return_type = opts[:debug_return_type] || 'LogActivityResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"LogsApi.get_project_log_activity", @@ -148,7 +148,7 @@ def search_project_logs_with_http_info(id, log_search_request, opts = {}) return_type = opts[:debug_return_type] || 'LogSearchResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"LogsApi.search_project_logs", @@ -228,7 +228,7 @@ def stream_project_logs_with_http_info(id, log_stream_request, opts = {}) return_type = opts[:debug_return_type] || 'String' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"LogsApi.stream_project_logs", diff --git a/lib/volcano/generated/lib/volcano-generated/api/o_auth_configuration_api.rb b/lib/volcano/generated/lib/volcano-generated/api/o_auth_configuration_api.rb index 2f8dfb13..77f5edf0 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/o_auth_configuration_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/o_auth_configuration_api.rb @@ -74,7 +74,7 @@ def create_o_auth_config_with_http_info(id, create_o_auth_config_request, opts = return_type = opts[:debug_return_type] || 'OAuthConfig' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"OAuthConfigurationApi.create_o_auth_config", @@ -147,7 +147,7 @@ def delete_o_auth_config_with_http_info(id, provider, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"OAuthConfigurationApi.delete_o_auth_config", @@ -222,7 +222,7 @@ def get_o_auth_config_with_http_info(id, provider, opts = {}) return_type = opts[:debug_return_type] || 'OAuthConfig' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"OAuthConfigurationApi.get_o_auth_config", @@ -285,7 +285,7 @@ def list_available_o_auth_providers_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ListAvailableOAuthProviders200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"OAuthConfigurationApi.list_available_o_auth_providers", @@ -348,7 +348,7 @@ def list_o_auth_configs_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ListOAuthConfigs200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"OAuthConfigurationApi.list_o_auth_configs", @@ -430,7 +430,7 @@ def update_o_auth_config_with_http_info(id, provider, opts = {}) return_type = opts[:debug_return_type] || 'OAuthConfig' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"OAuthConfigurationApi.update_o_auth_config", diff --git a/lib/volcano/generated/lib/volcano-generated/api/project_access_tokens_api.rb b/lib/volcano/generated/lib/volcano-generated/api/project_access_tokens_api.rb new file mode 100644 index 00000000..a0276cb4 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/api/project_access_tokens_api.rb @@ -0,0 +1,479 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'cgi' + +module Volcano::Generated + class ProjectAccessTokensApi + attr_accessor :api_client + + def initialize(api_client = ApiClient.default) + @api_client = api_client + end + # Create a project access token + # Creates a project access token and returns its secret. The secret is in this response and nowhere else. Only its hash is stored, so it cannot be retrieved, displayed, or recovered later — save it when you create it. The name must be unique within the project, so a retry cannot mint a second credential. It cannot recover the first one either. A retry that returns `409` with code `access_token_name_exists` means the original create committed and its secret is unrecoverable: list the project's tokens, revoke the one holding that name, and create it again. Requires a platform token. + # @param id [String] Project ID + # @param create_project_access_token_request [CreateProjectAccessTokenRequest] + # @param [Hash] opts the optional parameters + # @return [CreatedProjectAccessToken] + def create_project_access_token(id, create_project_access_token_request, opts = {}) + data, _status_code, _headers = create_project_access_token_with_http_info(id, create_project_access_token_request, opts) + data + end + + # Create a project access token + # Creates a project access token and returns its secret. The secret is in this response and nowhere else. Only its hash is stored, so it cannot be retrieved, displayed, or recovered later — save it when you create it. The name must be unique within the project, so a retry cannot mint a second credential. It cannot recover the first one either. A retry that returns `409` with code `access_token_name_exists` means the original create committed and its secret is unrecoverable: list the project's tokens, revoke the one holding that name, and create it again. Requires a platform token. + # @param id [String] Project ID + # @param create_project_access_token_request [CreateProjectAccessTokenRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(CreatedProjectAccessToken, Integer, Hash)>] CreatedProjectAccessToken data, response status code and response headers + def create_project_access_token_with_http_info(id, create_project_access_token_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: ProjectAccessTokensApi.create_project_access_token ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling ProjectAccessTokensApi.create_project_access_token" + end + # verify the required parameter 'create_project_access_token_request' is set + if @api_client.config.client_side_validation && create_project_access_token_request.nil? + fail ArgumentError, "Missing the required parameter 'create_project_access_token_request' when calling ProjectAccessTokensApi.create_project_access_token" + end + # resource path + local_var_path = '/projects/{id}/access-tokens'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(create_project_access_token_request) + + # return_type + return_type = opts[:debug_return_type] || 'CreatedProjectAccessToken' + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken'] + + new_options = opts.merge( + :operation => :"ProjectAccessTokensApi.create_project_access_token", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: ProjectAccessTokensApi#create_project_access_token\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Get a project access token + # Returns one token's metadata. Never its secret, which is not stored in a recoverable form. Requires a platform token. + # @param id [String] Project ID + # @param token_id [String] Project access token ID + # @param [Hash] opts the optional parameters + # @return [ProjectAccessToken] + def get_project_access_token(id, token_id, opts = {}) + data, _status_code, _headers = get_project_access_token_with_http_info(id, token_id, opts) + data + end + + # Get a project access token + # Returns one token's metadata. Never its secret, which is not stored in a recoverable form. Requires a platform token. + # @param id [String] Project ID + # @param token_id [String] Project access token ID + # @param [Hash] opts the optional parameters + # @return [Array<(ProjectAccessToken, Integer, Hash)>] ProjectAccessToken data, response status code and response headers + def get_project_access_token_with_http_info(id, token_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: ProjectAccessTokensApi.get_project_access_token ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling ProjectAccessTokensApi.get_project_access_token" + end + # verify the required parameter 'token_id' is set + if @api_client.config.client_side_validation && token_id.nil? + fail ArgumentError, "Missing the required parameter 'token_id' when calling ProjectAccessTokensApi.get_project_access_token" + end + # resource path + local_var_path = '/projects/{id}/access-tokens/{tokenId}'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'tokenId' + '}', CGI.escape(token_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'ProjectAccessToken' + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken'] + + new_options = opts.merge( + :operation => :"ProjectAccessTokensApi.get_project_access_token", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: ProjectAccessTokensApi#get_project_access_token\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Per-day request counts for one access token + # Returns a zero-filled daily series of request counts for a single token, oldest first, so the response always has exactly `days` entries. `days` defaults to 30 and is capped at 60, matching how long per-day counts are retained. A project access token may read only its own usage; asking for another token's returns `403`. A platform token may read any token in the project. + # @param id [String] Project ID + # @param token_id [String] Project access token ID + # @param [Hash] opts the optional parameters + # @option opts [Integer] :days Number of trailing days to return (1-60, default 30). (default to 30) + # @return [ProjectAccessTokenUsage] + def get_project_access_token_usage(id, token_id, opts = {}) + data, _status_code, _headers = get_project_access_token_usage_with_http_info(id, token_id, opts) + data + end + + # Per-day request counts for one access token + # Returns a zero-filled daily series of request counts for a single token, oldest first, so the response always has exactly `days` entries. `days` defaults to 30 and is capped at 60, matching how long per-day counts are retained. A project access token may read only its own usage; asking for another token's returns `403`. A platform token may read any token in the project. + # @param id [String] Project ID + # @param token_id [String] Project access token ID + # @param [Hash] opts the optional parameters + # @option opts [Integer] :days Number of trailing days to return (1-60, default 30). (default to 30) + # @return [Array<(ProjectAccessTokenUsage, Integer, Hash)>] ProjectAccessTokenUsage data, response status code and response headers + def get_project_access_token_usage_with_http_info(id, token_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: ProjectAccessTokensApi.get_project_access_token_usage ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling ProjectAccessTokensApi.get_project_access_token_usage" + end + # verify the required parameter 'token_id' is set + if @api_client.config.client_side_validation && token_id.nil? + fail ArgumentError, "Missing the required parameter 'token_id' when calling ProjectAccessTokensApi.get_project_access_token_usage" + end + if @api_client.config.client_side_validation && !opts[:'days'].nil? && opts[:'days'] > 60 + fail ArgumentError, 'invalid value for "opts[:"days"]" when calling ProjectAccessTokensApi.get_project_access_token_usage, must be smaller than or equal to 60.' + end + + if @api_client.config.client_side_validation && !opts[:'days'].nil? && opts[:'days'] < 1 + fail ArgumentError, 'invalid value for "opts[:"days"]" when calling ProjectAccessTokensApi.get_project_access_token_usage, must be greater than or equal to 1.' + end + + # resource path + local_var_path = '/projects/{id}/access-tokens/{tokenId}/usage'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'tokenId' + '}', CGI.escape(token_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + query_params[:'days'] = opts[:'days'] if !opts[:'days'].nil? + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'ProjectAccessTokenUsage' + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] + + new_options = opts.merge( + :operation => :"ProjectAccessTokensApi.get_project_access_token_usage", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: ProjectAccessTokensApi#get_project_access_token_usage\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # List a project's access tokens + # Lists the project's access tokens, newest first. Secrets are never returned: only a hash is stored, so a token's value exists solely in the response to the create call. Only tokens that can still authenticate are returned by default, so revoked and expired ones are hidden. Pass `include_revoked=true` to see them, which is how you find out what a key did before it stopped working. Requires a platform token. A project access token cannot manage project access tokens, so a leaked credential cannot enumerate or replace itself. + # @param id [String] Project ID + # @param [Hash] opts the optional parameters + # @option opts [Integer] :page Page number (1-indexed) for offset pagination. Declares no schema default so the request validator does not inject one: handlers that omit `page` see it unset (nil) and default to 1 in code, while cursor-first endpoints (e.g. the project deployments feed) can detect its absence to stay in keyset/search mode. Supplying `page` selects offset pagination. + # @option opts [Integer] :limit Number of items per page (max 100) (default to 10) + # @option opts [String] :search Case-insensitive substring match on the resource `name`. See the endpoint description for supported pagination modes. + # @option opts [Boolean] :include_revoked Include tokens that can no longer authenticate — both revoked and expired ones. (default to false) + # @return [PaginatedProjectAccessTokens] + def list_project_access_tokens(id, opts = {}) + data, _status_code, _headers = list_project_access_tokens_with_http_info(id, opts) + data + end + + # List a project's access tokens + # Lists the project's access tokens, newest first. Secrets are never returned: only a hash is stored, so a token's value exists solely in the response to the create call. Only tokens that can still authenticate are returned by default, so revoked and expired ones are hidden. Pass `include_revoked=true` to see them, which is how you find out what a key did before it stopped working. Requires a platform token. A project access token cannot manage project access tokens, so a leaked credential cannot enumerate or replace itself. + # @param id [String] Project ID + # @param [Hash] opts the optional parameters + # @option opts [Integer] :page Page number (1-indexed) for offset pagination. Declares no schema default so the request validator does not inject one: handlers that omit `page` see it unset (nil) and default to 1 in code, while cursor-first endpoints (e.g. the project deployments feed) can detect its absence to stay in keyset/search mode. Supplying `page` selects offset pagination. + # @option opts [Integer] :limit Number of items per page (max 100) (default to 10) + # @option opts [String] :search Case-insensitive substring match on the resource `name`. See the endpoint description for supported pagination modes. + # @option opts [Boolean] :include_revoked Include tokens that can no longer authenticate — both revoked and expired ones. (default to false) + # @return [Array<(PaginatedProjectAccessTokens, Integer, Hash)>] PaginatedProjectAccessTokens data, response status code and response headers + def list_project_access_tokens_with_http_info(id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: ProjectAccessTokensApi.list_project_access_tokens ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling ProjectAccessTokensApi.list_project_access_tokens" + end + if @api_client.config.client_side_validation && !opts[:'page'].nil? && opts[:'page'] < 1 + fail ArgumentError, 'invalid value for "opts[:"page"]" when calling ProjectAccessTokensApi.list_project_access_tokens, must be greater than or equal to 1.' + end + + if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100 + fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling ProjectAccessTokensApi.list_project_access_tokens, must be smaller than or equal to 100.' + end + + if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1 + fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling ProjectAccessTokensApi.list_project_access_tokens, must be greater than or equal to 1.' + end + + if @api_client.config.client_side_validation && !opts[:'search'].nil? && opts[:'search'].to_s.length > 256 + fail ArgumentError, 'invalid value for "opts[:"search"]" when calling ProjectAccessTokensApi.list_project_access_tokens, the character length must be smaller than or equal to 256.' + end + + # resource path + local_var_path = '/projects/{id}/access-tokens'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + query_params[:'page'] = opts[:'page'] if !opts[:'page'].nil? + query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil? + query_params[:'search'] = opts[:'search'] if !opts[:'search'].nil? + query_params[:'include_revoked'] = opts[:'include_revoked'] if !opts[:'include_revoked'].nil? + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'PaginatedProjectAccessTokens' + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken'] + + new_options = opts.merge( + :operation => :"ProjectAccessTokensApi.list_project_access_tokens", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: ProjectAccessTokensApi#list_project_access_tokens\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Per-day request counts for every access token in a project + # Returns a zero-filled daily series of request counts for each of the project's access tokens, oldest first. Every day in the window is present, so a gap reads as zero rather than missing. Revoked tokens are included, because the traffic they made before revocation is usually the reason you are looking. `days` defaults to 30 and is capped at 60, which is also how long per-day counts are retained — a longer window cannot be answered. A platform token sees every token in the project. A project access token sees only its own row, so it can watch its own traffic without being able to enumerate the project's other credentials by name. + # @param id [String] Project ID + # @param [Hash] opts the optional parameters + # @option opts [Integer] :days Number of trailing days to return (1-60, default 30). (default to 30) + # @return [Array] + def list_project_access_tokens_usage(id, opts = {}) + data, _status_code, _headers = list_project_access_tokens_usage_with_http_info(id, opts) + data + end + + # Per-day request counts for every access token in a project + # Returns a zero-filled daily series of request counts for each of the project's access tokens, oldest first. Every day in the window is present, so a gap reads as zero rather than missing. Revoked tokens are included, because the traffic they made before revocation is usually the reason you are looking. `days` defaults to 30 and is capped at 60, which is also how long per-day counts are retained — a longer window cannot be answered. A platform token sees every token in the project. A project access token sees only its own row, so it can watch its own traffic without being able to enumerate the project's other credentials by name. + # @param id [String] Project ID + # @param [Hash] opts the optional parameters + # @option opts [Integer] :days Number of trailing days to return (1-60, default 30). (default to 30) + # @return [Array<(Array, Integer, Hash)>] Array data, response status code and response headers + def list_project_access_tokens_usage_with_http_info(id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: ProjectAccessTokensApi.list_project_access_tokens_usage ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling ProjectAccessTokensApi.list_project_access_tokens_usage" + end + if @api_client.config.client_side_validation && !opts[:'days'].nil? && opts[:'days'] > 60 + fail ArgumentError, 'invalid value for "opts[:"days"]" when calling ProjectAccessTokensApi.list_project_access_tokens_usage, must be smaller than or equal to 60.' + end + + if @api_client.config.client_side_validation && !opts[:'days'].nil? && opts[:'days'] < 1 + fail ArgumentError, 'invalid value for "opts[:"days"]" when calling ProjectAccessTokensApi.list_project_access_tokens_usage, must be greater than or equal to 1.' + end + + # resource path + local_var_path = '/projects/{id}/access-tokens/usage'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + query_params[:'days'] = opts[:'days'] if !opts[:'days'].nil? + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'Array' + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] + + new_options = opts.merge( + :operation => :"ProjectAccessTokensApi.list_project_access_tokens_usage", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: ProjectAccessTokensApi#list_project_access_tokens_usage\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Revoke a project access token + # Revokes the token. It stops authenticating immediately in the region handling this call and within seconds across Volcano's other regions. The record is kept rather than deleted, so the token's name, prefix, last use, and request history stay available — which is what you need if you are revoking because a secret leaked. Revoking an already-revoked token succeeds. Revoking does not undo anything the token already did. Treat whatever it could reach as exposed and rotate accordingly. Requires a platform token. + # @param id [String] Project ID + # @param token_id [String] Project access token ID + # @param [Hash] opts the optional parameters + # @return [nil] + def revoke_project_access_token(id, token_id, opts = {}) + revoke_project_access_token_with_http_info(id, token_id, opts) + nil + end + + # Revoke a project access token + # Revokes the token. It stops authenticating immediately in the region handling this call and within seconds across Volcano's other regions. The record is kept rather than deleted, so the token's name, prefix, last use, and request history stay available — which is what you need if you are revoking because a secret leaked. Revoking an already-revoked token succeeds. Revoking does not undo anything the token already did. Treat whatever it could reach as exposed and rotate accordingly. Requires a platform token. + # @param id [String] Project ID + # @param token_id [String] Project access token ID + # @param [Hash] opts the optional parameters + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def revoke_project_access_token_with_http_info(id, token_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: ProjectAccessTokensApi.revoke_project_access_token ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling ProjectAccessTokensApi.revoke_project_access_token" + end + # verify the required parameter 'token_id' is set + if @api_client.config.client_side_validation && token_id.nil? + fail ArgumentError, "Missing the required parameter 'token_id' when calling ProjectAccessTokensApi.revoke_project_access_token" + end + # resource path + local_var_path = '/projects/{id}/access-tokens/{tokenId}'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'tokenId' + '}', CGI.escape(token_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken'] + + new_options = opts.merge( + :operation => :"ProjectAccessTokensApi.revoke_project_access_token", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:DELETE, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: ProjectAccessTokensApi#revoke_project_access_token\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + end +end diff --git a/lib/volcano/generated/lib/volcano-generated/api/projects_api.rb b/lib/volcano/generated/lib/volcano-generated/api/projects_api.rb index 31a61717..66d6c7f0 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/projects_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/projects_api.rb @@ -77,7 +77,7 @@ def apply_project_config_with_http_info(id, project_config, opts = {}) return_type = opts[:debug_return_type] || 'ProjectConfigApplyResult' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.apply_project_config", @@ -140,7 +140,7 @@ def cancel_project_source_export_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.cancel_project_source_export", @@ -214,7 +214,7 @@ def connect_project_git_with_http_info(id, connect_project_git_request, opts = { return_type = opts[:debug_return_type] || 'ProjectGitConnection' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.connect_project_git", @@ -345,7 +345,7 @@ def delete_project_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.delete_project", @@ -408,7 +408,7 @@ def delete_project_logo_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'Project' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.delete_project_logo", @@ -469,7 +469,7 @@ def disconnect_project_git_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.disconnect_project_git", @@ -543,7 +543,7 @@ def export_project_source_with_http_info(id, export_project_source_request, opts return_type = opts[:debug_return_type] || 'ProjectSourceExport' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.export_project_source", @@ -604,7 +604,7 @@ def get_project_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'Project' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.get_project", @@ -624,7 +624,7 @@ def get_project_with_http_info(id, opts = {}) end # Export project configuration - # Exports the project's current user-facing configuration as a declarative manifest. Returns JSON by default. Request the canonical volcano-config.yaml rendering with `Accept: application/yaml` or `?format=yaml`; the YAML is returned verbatim as the raw response body (`Content-Type: application/yaml`) and is meant to be saved as-is. Variable values and write-only secrets (SMTP password, OAuth client secrets, TLS material) are omitted from the export; shared_variables contains names only; the YAML rendering adds a header comment describing how to set them via CLI environment interpolation. + # Exports the project's current user-facing configuration as a declarative manifest. Returns JSON by default. Request the canonical volcano-config.yaml rendering with `Accept: application/yaml` or `?format=yaml`; the YAML is returned verbatim as the raw response body (`Content-Type: application/yaml`) and is meant to be saved as-is. Variable values and write-only secrets (SMTP password, OAuth client secrets, TLS material) are omitted from the export; shared_variables and frontend_shared_variables contain names only; the YAML rendering adds a header comment describing how to set them via CLI environment interpolation. # @param id [String] Project ID # @param [Hash] opts the optional parameters # @option opts [String] :format Response format override. Takes precedence over the Accept header. @@ -635,7 +635,7 @@ def get_project_config(id, opts = {}) end # Export project configuration - # Exports the project's current user-facing configuration as a declarative manifest. Returns JSON by default. Request the canonical volcano-config.yaml rendering with `Accept: application/yaml` or `?format=yaml`; the YAML is returned verbatim as the raw response body (`Content-Type: application/yaml`) and is meant to be saved as-is. Variable values and write-only secrets (SMTP password, OAuth client secrets, TLS material) are omitted from the export; shared_variables contains names only; the YAML rendering adds a header comment describing how to set them via CLI environment interpolation. + # Exports the project's current user-facing configuration as a declarative manifest. Returns JSON by default. Request the canonical volcano-config.yaml rendering with `Accept: application/yaml` or `?format=yaml`; the YAML is returned verbatim as the raw response body (`Content-Type: application/yaml`) and is meant to be saved as-is. Variable values and write-only secrets (SMTP password, OAuth client secrets, TLS material) are omitted from the export; shared_variables and frontend_shared_variables contain names only; the YAML rendering adds a header comment describing how to set them via CLI environment interpolation. # @param id [String] Project ID # @param [Hash] opts the optional parameters # @option opts [String] :format Response format override. Takes precedence over the Accept header. @@ -674,7 +674,7 @@ def get_project_config_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectConfig' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.get_project_config", @@ -735,7 +735,7 @@ def get_project_git_connection_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectGitConnection' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.get_project_git_connection", @@ -796,7 +796,7 @@ def get_project_git_deploy_settings_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectGitDeploySettings' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.get_project_git_deploy_settings", @@ -859,7 +859,7 @@ def get_project_health_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectHealthResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.get_project_health", @@ -985,7 +985,7 @@ def get_project_source_export_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectSourceExportState' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.get_project_source_export", @@ -1048,7 +1048,7 @@ def get_project_usage_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ProjectUsageResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.get_project_usage", @@ -1288,7 +1288,7 @@ def list_project_deployments_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedProjectDeployments' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.list_project_deployments", @@ -1464,7 +1464,7 @@ def query_project_metrics_with_http_info(id, project_metrics_query_request, opts return_type = opts[:debug_return_type] || 'ProjectMetricsQueryResponse' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.query_project_metrics", @@ -1483,6 +1483,80 @@ def query_project_metrics_with_http_info(id, project_metrics_query_request, opts return data, status_code, headers end + # Replace frontend shared variable names + # Atomically replaces the complete shared frontend-variable list without changing values. Names must already exist. Validates final affected frontend environments before membership or propagation side effects. An empty list clears membership. Omitted names remain stored outside the frontend shared list. + # @param id [String] Project ID + # @param replace_frontend_shared_variables_request [ReplaceFrontendSharedVariablesRequest] + # @param [Hash] opts the optional parameters + # @return [nil] + def replace_frontend_shared_variables(id, replace_frontend_shared_variables_request, opts = {}) + replace_frontend_shared_variables_with_http_info(id, replace_frontend_shared_variables_request, opts) + nil + end + + # Replace frontend shared variable names + # Atomically replaces the complete shared frontend-variable list without changing values. Names must already exist. Validates final affected frontend environments before membership or propagation side effects. An empty list clears membership. Omitted names remain stored outside the frontend shared list. + # @param id [String] Project ID + # @param replace_frontend_shared_variables_request [ReplaceFrontendSharedVariablesRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def replace_frontend_shared_variables_with_http_info(id, replace_frontend_shared_variables_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: ProjectsApi.replace_frontend_shared_variables ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling ProjectsApi.replace_frontend_shared_variables" + end + # verify the required parameter 'replace_frontend_shared_variables_request' is set + if @api_client.config.client_side_validation && replace_frontend_shared_variables_request.nil? + fail ArgumentError, "Missing the required parameter 'replace_frontend_shared_variables_request' when calling ProjectsApi.replace_frontend_shared_variables" + end + # resource path + local_var_path = '/projects/{id}/frontend-shared-variables'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(replace_frontend_shared_variables_request) + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || ['UserToken'] + + new_options = opts.merge( + :operation => :"ProjectsApi.replace_frontend_shared_variables", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:PUT, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: ProjectsApi#replace_frontend_shared_variables\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + # Replace shared variable names # Atomically replaces the complete shared function-variable list without changing values. Names must already exist. Validates final affected function environments before membership or propagation side effects. An empty list clears membership. Omitted names remain stored as non-shared variables. # @param id [String] Project ID @@ -1538,7 +1612,7 @@ def replace_shared_variables_with_http_info(id, replace_shared_variables_request return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.replace_shared_variables", @@ -1612,7 +1686,7 @@ def set_project_git_production_branch_with_http_info(id, set_project_git_product return_type = opts[:debug_return_type] || 'ProjectGitConnection' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.set_project_git_production_branch", @@ -1693,7 +1767,7 @@ def summarize_project_deployments_with_http_info(id, resource_type, opts = {}) return_type = opts[:debug_return_type] || 'ProjectDeploymentSummary' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.summarize_project_deployments", @@ -1765,7 +1839,7 @@ def update_project_with_http_info(id, update_project_request, opts = {}) return_type = opts[:debug_return_type] || 'Project' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.update_project", @@ -1839,7 +1913,7 @@ def update_project_git_deploy_settings_with_http_info(id, update_project_git_dep return_type = opts[:debug_return_type] || 'ProjectGitDeploySettings' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.update_project_git_deploy_settings", @@ -1914,7 +1988,7 @@ def upload_project_logo_with_http_info(id, logo, opts = {}) return_type = opts[:debug_return_type] || 'Project' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ProjectsApi.upload_project_logo", diff --git a/lib/volcano/generated/lib/volcano-generated/api/realtime_api.rb b/lib/volcano/generated/lib/volcano-generated/api/realtime_api.rb index dd7a821a..4d7cf802 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/realtime_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/realtime_api.rb @@ -63,7 +63,7 @@ def get_realtime_config_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'RealtimeConfig' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"RealtimeApi.get_realtime_config", @@ -126,7 +126,7 @@ def get_realtime_stats_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'RealtimeStats' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"RealtimeApi.get_realtime_stats", @@ -200,7 +200,7 @@ def update_realtime_config_with_http_info(id, update_realtime_config_request, op return_type = opts[:debug_return_type] || 'RealtimeConfig' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"RealtimeApi.update_realtime_config", diff --git a/lib/volcano/generated/lib/volcano-generated/api/sandboxes_api.rb b/lib/volcano/generated/lib/volcano-generated/api/sandboxes_api.rb new file mode 100644 index 00000000..3e524f53 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/api/sandboxes_api.rb @@ -0,0 +1,1433 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'cgi' + +module Volcano::Generated + class SandboxesApi + attr_accessor :api_client + + def initialize(api_client = ApiClient.default) + @api_client = api_client + end + # Create a sandbox template from a verified preset + # @param id [String] + # @param idempotency_key [String] + # @param create_sandbox_template_request [CreateSandboxTemplateRequest] + # @param [Hash] opts the optional parameters + # @return [SandboxTemplate] + def create_sandbox(id, idempotency_key, create_sandbox_template_request, opts = {}) + data, _status_code, _headers = create_sandbox_with_http_info(id, idempotency_key, create_sandbox_template_request, opts) + data + end + + # Create a sandbox template from a verified preset + # @param id [String] + # @param idempotency_key [String] + # @param create_sandbox_template_request [CreateSandboxTemplateRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxTemplate, Integer, Hash)>] SandboxTemplate data, response status code and response headers + def create_sandbox_with_http_info(id, idempotency_key, create_sandbox_template_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.create_sandbox ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.create_sandbox" + end + # verify the required parameter 'idempotency_key' is set + if @api_client.config.client_side_validation && idempotency_key.nil? + fail ArgumentError, "Missing the required parameter 'idempotency_key' when calling SandboxesApi.create_sandbox" + end + # verify the required parameter 'create_sandbox_template_request' is set + if @api_client.config.client_side_validation && create_sandbox_template_request.nil? + fail ArgumentError, "Missing the required parameter 'create_sandbox_template_request' when calling SandboxesApi.create_sandbox" + end + # resource path + local_var_path = '/projects/{id}/sandboxes'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + header_params[:'Idempotency-Key'] = idempotency_key + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(create_sandbox_template_request) + + # return_type + return_type = opts[:debug_return_type] || 'SandboxTemplate' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.create_sandbox", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#create_sandbox\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Start a sandbox session + # @param id [String] + # @param idempotency_key [String] + # @param create_sandbox_session_request [CreateSandboxSessionRequest] + # @param [Hash] opts the optional parameters + # @return [SandboxSession] + def create_sandbox_session(id, idempotency_key, create_sandbox_session_request, opts = {}) + data, _status_code, _headers = create_sandbox_session_with_http_info(id, idempotency_key, create_sandbox_session_request, opts) + data + end + + # Start a sandbox session + # @param id [String] + # @param idempotency_key [String] + # @param create_sandbox_session_request [CreateSandboxSessionRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxSession, Integer, Hash)>] SandboxSession data, response status code and response headers + def create_sandbox_session_with_http_info(id, idempotency_key, create_sandbox_session_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.create_sandbox_session ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.create_sandbox_session" + end + # verify the required parameter 'idempotency_key' is set + if @api_client.config.client_side_validation && idempotency_key.nil? + fail ArgumentError, "Missing the required parameter 'idempotency_key' when calling SandboxesApi.create_sandbox_session" + end + # resource path + local_var_path = '/projects/{id}/sandbox-sessions'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + header_params[:'Idempotency-Key'] = idempotency_key + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(create_sandbox_session_request) + + # return_type + return_type = opts[:debug_return_type] || 'SandboxSession' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.create_sandbox_session", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#create_sandbox_session\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Issue a short-lived port-scoped access credential + # @param session_id [String] + # @param sandbox_access_request [SandboxAccessRequest] + # @param [Hash] opts the optional parameters + # @return [SandboxAccess] + def create_sandbox_session_access(session_id, sandbox_access_request, opts = {}) + data, _status_code, _headers = create_sandbox_session_access_with_http_info(session_id, sandbox_access_request, opts) + data + end + + # Issue a short-lived port-scoped access credential + # @param session_id [String] + # @param sandbox_access_request [SandboxAccessRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxAccess, Integer, Hash)>] SandboxAccess data, response status code and response headers + def create_sandbox_session_access_with_http_info(session_id, sandbox_access_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.create_sandbox_session_access ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.create_sandbox_session_access" + end + # verify the required parameter 'sandbox_access_request' is set + if @api_client.config.client_side_validation && sandbox_access_request.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_access_request' when calling SandboxesApi.create_sandbox_session_access" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}/access'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(sandbox_access_request) + + # return_type + return_type = opts[:debug_return_type] || 'SandboxAccess' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'AuthUserAccessToken', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.create_sandbox_session_access", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#create_sandbox_session_access\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Retire a template and terminate its sessions + # @param id [String] + # @param sandbox_id [String] + # @param [Hash] opts the optional parameters + # @return [nil] + def delete_sandbox(id, sandbox_id, opts = {}) + delete_sandbox_with_http_info(id, sandbox_id, opts) + nil + end + + # Retire a template and terminate its sessions + # @param id [String] + # @param sandbox_id [String] + # @param [Hash] opts the optional parameters + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def delete_sandbox_with_http_info(id, sandbox_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.delete_sandbox ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.delete_sandbox" + end + # verify the required parameter 'sandbox_id' is set + if @api_client.config.client_side_validation && sandbox_id.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_id' when calling SandboxesApi.delete_sandbox" + end + # resource path + local_var_path = '/projects/{id}/sandboxes/{sandboxId}'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'sandboxId' + '}', CGI.escape(sandbox_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.delete_sandbox", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:DELETE, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#delete_sandbox\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Execute once and return after confirmed termination + # @param id [String] + # @param idempotency_key [String] + # @param sandbox_execution_request [SandboxExecutionRequest] + # @param [Hash] opts the optional parameters + # @return [SandboxExecutionResult] + def execute_sandbox(id, idempotency_key, sandbox_execution_request, opts = {}) + data, _status_code, _headers = execute_sandbox_with_http_info(id, idempotency_key, sandbox_execution_request, opts) + data + end + + # Execute once and return after confirmed termination + # @param id [String] + # @param idempotency_key [String] + # @param sandbox_execution_request [SandboxExecutionRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxExecutionResult, Integer, Hash)>] SandboxExecutionResult data, response status code and response headers + def execute_sandbox_with_http_info(id, idempotency_key, sandbox_execution_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.execute_sandbox ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.execute_sandbox" + end + # verify the required parameter 'idempotency_key' is set + if @api_client.config.client_side_validation && idempotency_key.nil? + fail ArgumentError, "Missing the required parameter 'idempotency_key' when calling SandboxesApi.execute_sandbox" + end + # resource path + local_var_path = '/projects/{id}/sandbox-executions'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + header_params[:'Idempotency-Key'] = idempotency_key + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(sandbox_execution_request) + + # return_type + return_type = opts[:debug_return_type] || 'SandboxExecutionResult' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.execute_sandbox", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#execute_sandbox\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Execute a command within a session + # @param session_id [String] + # @param idempotency_key [String] + # @param sandbox_command_request [SandboxCommandRequest] + # @param [Hash] opts the optional parameters + # @return [SandboxCommandResult] + def execute_sandbox_session(session_id, idempotency_key, sandbox_command_request, opts = {}) + data, _status_code, _headers = execute_sandbox_session_with_http_info(session_id, idempotency_key, sandbox_command_request, opts) + data + end + + # Execute a command within a session + # @param session_id [String] + # @param idempotency_key [String] + # @param sandbox_command_request [SandboxCommandRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxCommandResult, Integer, Hash)>] SandboxCommandResult data, response status code and response headers + def execute_sandbox_session_with_http_info(session_id, idempotency_key, sandbox_command_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.execute_sandbox_session ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.execute_sandbox_session" + end + # verify the required parameter 'idempotency_key' is set + if @api_client.config.client_side_validation && idempotency_key.nil? + fail ArgumentError, "Missing the required parameter 'idempotency_key' when calling SandboxesApi.execute_sandbox_session" + end + # verify the required parameter 'sandbox_command_request' is set + if @api_client.config.client_side_validation && sandbox_command_request.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_command_request' when calling SandboxesApi.execute_sandbox_session" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}/exec'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + header_params[:'Idempotency-Key'] = idempotency_key + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(sandbox_command_request) + + # return_type + return_type = opts[:debug_return_type] || 'SandboxCommandResult' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'AuthUserAccessToken', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.execute_sandbox_session", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#execute_sandbox_session\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Get a sandbox template + # @param id [String] + # @param sandbox_id [String] + # @param [Hash] opts the optional parameters + # @return [SandboxTemplate] + def get_sandbox(id, sandbox_id, opts = {}) + data, _status_code, _headers = get_sandbox_with_http_info(id, sandbox_id, opts) + data + end + + # Get a sandbox template + # @param id [String] + # @param sandbox_id [String] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxTemplate, Integer, Hash)>] SandboxTemplate data, response status code and response headers + def get_sandbox_with_http_info(id, sandbox_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.get_sandbox ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.get_sandbox" + end + # verify the required parameter 'sandbox_id' is set + if @api_client.config.client_side_validation && sandbox_id.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_id' when calling SandboxesApi.get_sandbox" + end + # resource path + local_var_path = '/projects/{id}/sandboxes/{sandboxId}'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'sandboxId' + '}', CGI.escape(sandbox_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxTemplate' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.get_sandbox", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#get_sandbox\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Get a sandbox session + # @param session_id [String] + # @param [Hash] opts the optional parameters + # @return [SandboxSession] + def get_sandbox_session(session_id, opts = {}) + data, _status_code, _headers = get_sandbox_session_with_http_info(session_id, opts) + data + end + + # Get a sandbox session + # @param session_id [String] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxSession, Integer, Hash)>] SandboxSession data, response status code and response headers + def get_sandbox_session_with_http_info(session_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.get_sandbox_session ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.get_sandbox_session" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxSession' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'AuthUserAccessToken', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.get_sandbox_session", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#get_sandbox_session\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Authorize an authenticated project user for this session + # @param session_id [String] + # @param subject_id [String] + # @param sandbox_subject_grant_request [SandboxSubjectGrantRequest] + # @param [Hash] opts the optional parameters + # @return [nil] + def grant_sandbox_session(session_id, subject_id, sandbox_subject_grant_request, opts = {}) + grant_sandbox_session_with_http_info(session_id, subject_id, sandbox_subject_grant_request, opts) + nil + end + + # Authorize an authenticated project user for this session + # @param session_id [String] + # @param subject_id [String] + # @param sandbox_subject_grant_request [SandboxSubjectGrantRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def grant_sandbox_session_with_http_info(session_id, subject_id, sandbox_subject_grant_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.grant_sandbox_session ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.grant_sandbox_session" + end + # verify the required parameter 'subject_id' is set + if @api_client.config.client_side_validation && subject_id.nil? + fail ArgumentError, "Missing the required parameter 'subject_id' when calling SandboxesApi.grant_sandbox_session" + end + # verify the required parameter 'sandbox_subject_grant_request' is set + if @api_client.config.client_side_validation && sandbox_subject_grant_request.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_subject_grant_request' when calling SandboxesApi.grant_sandbox_session" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}/grants/{subjectId}'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)).sub('{' + 'subjectId' + '}', CGI.escape(subject_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(sandbox_subject_grant_request) + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.grant_sandbox_session", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:PUT, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#grant_sandbox_session\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # List sandbox deployment history + # @param id [String] + # @param sandbox_id [String] + # @param [Hash] opts the optional parameters + # @option opts [Integer] :limit Number of items per page (max 100) (default to 10) + # @option opts [String] :cursor Opaque keyset pagination cursor from a previous response's `next_cursor` — pages forward. Mutually exclusive with `page` and `ending_before`; combining them returns 400. When supplied, the request's `search` and `limit` must match the values bound to the cursor or the request returns 400. + # @return [SandboxDeploymentPage] + def list_sandbox_deployments(id, sandbox_id, opts = {}) + data, _status_code, _headers = list_sandbox_deployments_with_http_info(id, sandbox_id, opts) + data + end + + # List sandbox deployment history + # @param id [String] + # @param sandbox_id [String] + # @param [Hash] opts the optional parameters + # @option opts [Integer] :limit Number of items per page (max 100) (default to 10) + # @option opts [String] :cursor Opaque keyset pagination cursor from a previous response's `next_cursor` — pages forward. Mutually exclusive with `page` and `ending_before`; combining them returns 400. When supplied, the request's `search` and `limit` must match the values bound to the cursor or the request returns 400. + # @return [Array<(SandboxDeploymentPage, Integer, Hash)>] SandboxDeploymentPage data, response status code and response headers + def list_sandbox_deployments_with_http_info(id, sandbox_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.list_sandbox_deployments ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.list_sandbox_deployments" + end + # verify the required parameter 'sandbox_id' is set + if @api_client.config.client_side_validation && sandbox_id.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_id' when calling SandboxesApi.list_sandbox_deployments" + end + if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100 + fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling SandboxesApi.list_sandbox_deployments, must be smaller than or equal to 100.' + end + + if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1 + fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling SandboxesApi.list_sandbox_deployments, must be greater than or equal to 1.' + end + + # resource path + local_var_path = '/projects/{id}/sandboxes/{sandboxId}/deployments'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'sandboxId' + '}', CGI.escape(sandbox_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil? + query_params[:'cursor'] = opts[:'cursor'] if !opts[:'cursor'].nil? + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxDeploymentPage' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.list_sandbox_deployments", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#list_sandbox_deployments\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # List available sandbox presets + # @param [Hash] opts the optional parameters + # @return [SandboxPresetList] + def list_sandbox_presets(opts = {}) + data, _status_code, _headers = list_sandbox_presets_with_http_info(opts) + data + end + + # List available sandbox presets + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxPresetList, Integer, Hash)>] SandboxPresetList data, response status code and response headers + def list_sandbox_presets_with_http_info(opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.list_sandbox_presets ...' + end + # resource path + local_var_path = '/sandboxes/presets' + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxPresetList' + + # auth_names + auth_names = opts[:debug_auth_names] || [] + + new_options = opts.merge( + :operation => :"SandboxesApi.list_sandbox_presets", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#list_sandbox_presets\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # List project sandbox sessions + # @param id [String] + # @param [Hash] opts the optional parameters + # @option opts [Integer] :limit Number of items per page (max 100) (default to 10) + # @option opts [String] :cursor Opaque keyset pagination cursor from a previous response's `next_cursor` — pages forward. Mutually exclusive with `page` and `ending_before`; combining them returns 400. When supplied, the request's `search` and `limit` must match the values bound to the cursor or the request returns 400. + # @return [SandboxSessionPage] + def list_sandbox_sessions(id, opts = {}) + data, _status_code, _headers = list_sandbox_sessions_with_http_info(id, opts) + data + end + + # List project sandbox sessions + # @param id [String] + # @param [Hash] opts the optional parameters + # @option opts [Integer] :limit Number of items per page (max 100) (default to 10) + # @option opts [String] :cursor Opaque keyset pagination cursor from a previous response's `next_cursor` — pages forward. Mutually exclusive with `page` and `ending_before`; combining them returns 400. When supplied, the request's `search` and `limit` must match the values bound to the cursor or the request returns 400. + # @return [Array<(SandboxSessionPage, Integer, Hash)>] SandboxSessionPage data, response status code and response headers + def list_sandbox_sessions_with_http_info(id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.list_sandbox_sessions ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.list_sandbox_sessions" + end + if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100 + fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling SandboxesApi.list_sandbox_sessions, must be smaller than or equal to 100.' + end + + if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1 + fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling SandboxesApi.list_sandbox_sessions, must be greater than or equal to 1.' + end + + # resource path + local_var_path = '/projects/{id}/sandbox-sessions'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil? + query_params[:'cursor'] = opts[:'cursor'] if !opts[:'cursor'].nil? + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxSessionPage' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.list_sandbox_sessions", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#list_sandbox_sessions\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # List sandbox templates + # @param id [String] + # @param [Hash] opts the optional parameters + # @option opts [Integer] :limit Number of items per page (max 100) (default to 10) + # @option opts [String] :cursor Opaque keyset pagination cursor from a previous response's `next_cursor` — pages forward. Mutually exclusive with `page` and `ending_before`; combining them returns 400. When supplied, the request's `search` and `limit` must match the values bound to the cursor or the request returns 400. + # @return [SandboxTemplatePage] + def list_sandboxes(id, opts = {}) + data, _status_code, _headers = list_sandboxes_with_http_info(id, opts) + data + end + + # List sandbox templates + # @param id [String] + # @param [Hash] opts the optional parameters + # @option opts [Integer] :limit Number of items per page (max 100) (default to 10) + # @option opts [String] :cursor Opaque keyset pagination cursor from a previous response's `next_cursor` — pages forward. Mutually exclusive with `page` and `ending_before`; combining them returns 400. When supplied, the request's `search` and `limit` must match the values bound to the cursor or the request returns 400. + # @return [Array<(SandboxTemplatePage, Integer, Hash)>] SandboxTemplatePage data, response status code and response headers + def list_sandboxes_with_http_info(id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.list_sandboxes ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.list_sandboxes" + end + if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100 + fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling SandboxesApi.list_sandboxes, must be smaller than or equal to 100.' + end + + if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1 + fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling SandboxesApi.list_sandboxes, must be greater than or equal to 1.' + end + + # resource path + local_var_path = '/projects/{id}/sandboxes'.sub('{' + 'id' + '}', CGI.escape(id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil? + query_params[:'cursor'] = opts[:'cursor'] if !opts[:'cursor'].nil? + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxTemplatePage' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.list_sandboxes", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#list_sandboxes\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Read a workspace file + # @param session_id [String] + # @param sandbox_file_read_request [SandboxFileReadRequest] + # @param [Hash] opts the optional parameters + # @return [SandboxFileResult] + def read_sandbox_session_file(session_id, sandbox_file_read_request, opts = {}) + data, _status_code, _headers = read_sandbox_session_file_with_http_info(session_id, sandbox_file_read_request, opts) + data + end + + # Read a workspace file + # @param session_id [String] + # @param sandbox_file_read_request [SandboxFileReadRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxFileResult, Integer, Hash)>] SandboxFileResult data, response status code and response headers + def read_sandbox_session_file_with_http_info(session_id, sandbox_file_read_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.read_sandbox_session_file ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.read_sandbox_session_file" + end + # verify the required parameter 'sandbox_file_read_request' is set + if @api_client.config.client_side_validation && sandbox_file_read_request.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_file_read_request' when calling SandboxesApi.read_sandbox_session_file" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}/files/read'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(sandbox_file_read_request) + + # return_type + return_type = opts[:debug_return_type] || 'SandboxFileResult' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'AuthUserAccessToken', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.read_sandbox_session_file", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#read_sandbox_session_file\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Resume a sandbox session + # @param session_id [String] + # @param [Hash] opts the optional parameters + # @return [SandboxSession] + def resume_sandbox_session(session_id, opts = {}) + data, _status_code, _headers = resume_sandbox_session_with_http_info(session_id, opts) + data + end + + # Resume a sandbox session + # @param session_id [String] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxSession, Integer, Hash)>] SandboxSession data, response status code and response headers + def resume_sandbox_session_with_http_info(session_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.resume_sandbox_session ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.resume_sandbox_session" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}/resume'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxSession' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.resume_sandbox_session", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#resume_sandbox_session\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Revoke a project user session grant + # @param session_id [String] + # @param subject_id [String] + # @param [Hash] opts the optional parameters + # @return [nil] + def revoke_sandbox_session(session_id, subject_id, opts = {}) + revoke_sandbox_session_with_http_info(session_id, subject_id, opts) + nil + end + + # Revoke a project user session grant + # @param session_id [String] + # @param subject_id [String] + # @param [Hash] opts the optional parameters + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def revoke_sandbox_session_with_http_info(session_id, subject_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.revoke_sandbox_session ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.revoke_sandbox_session" + end + # verify the required parameter 'subject_id' is set + if @api_client.config.client_side_validation && subject_id.nil? + fail ArgumentError, "Missing the required parameter 'subject_id' when calling SandboxesApi.revoke_sandbox_session" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}/grants/{subjectId}'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)).sub('{' + 'subjectId' + '}', CGI.escape(subject_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.revoke_sandbox_session", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:DELETE, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#revoke_sandbox_session\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Suspend a sandbox session + # @param session_id [String] + # @param [Hash] opts the optional parameters + # @return [SandboxSession] + def suspend_sandbox_session(session_id, opts = {}) + data, _status_code, _headers = suspend_sandbox_session_with_http_info(session_id, opts) + data + end + + # Suspend a sandbox session + # @param session_id [String] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxSession, Integer, Hash)>] SandboxSession data, response status code and response headers + def suspend_sandbox_session_with_http_info(session_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.suspend_sandbox_session ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.suspend_sandbox_session" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}/suspend'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxSession' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.suspend_sandbox_session", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#suspend_sandbox_session\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Request sandbox termination + # @param session_id [String] + # @param [Hash] opts the optional parameters + # @return [SandboxSession] + def terminate_sandbox_session(session_id, opts = {}) + data, _status_code, _headers = terminate_sandbox_session_with_http_info(session_id, opts) + data + end + + # Request sandbox termination + # @param session_id [String] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxSession, Integer, Hash)>] SandboxSession data, response status code and response headers + def terminate_sandbox_session_with_http_info(session_id, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.terminate_sandbox_session ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.terminate_sandbox_session" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'SandboxSession' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.terminate_sandbox_session", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:DELETE, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#terminate_sandbox_session\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Rename a sandbox template + # @param id [String] + # @param sandbox_id [String] + # @param update_sandbox_template_request [UpdateSandboxTemplateRequest] + # @param [Hash] opts the optional parameters + # @return [SandboxTemplate] + def update_sandbox(id, sandbox_id, update_sandbox_template_request, opts = {}) + data, _status_code, _headers = update_sandbox_with_http_info(id, sandbox_id, update_sandbox_template_request, opts) + data + end + + # Rename a sandbox template + # @param id [String] + # @param sandbox_id [String] + # @param update_sandbox_template_request [UpdateSandboxTemplateRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(SandboxTemplate, Integer, Hash)>] SandboxTemplate data, response status code and response headers + def update_sandbox_with_http_info(id, sandbox_id, update_sandbox_template_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.update_sandbox ...' + end + # verify the required parameter 'id' is set + if @api_client.config.client_side_validation && id.nil? + fail ArgumentError, "Missing the required parameter 'id' when calling SandboxesApi.update_sandbox" + end + # verify the required parameter 'sandbox_id' is set + if @api_client.config.client_side_validation && sandbox_id.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_id' when calling SandboxesApi.update_sandbox" + end + # verify the required parameter 'update_sandbox_template_request' is set + if @api_client.config.client_side_validation && update_sandbox_template_request.nil? + fail ArgumentError, "Missing the required parameter 'update_sandbox_template_request' when calling SandboxesApi.update_sandbox" + end + # resource path + local_var_path = '/projects/{id}/sandboxes/{sandboxId}'.sub('{' + 'id' + '}', CGI.escape(id.to_s)).sub('{' + 'sandboxId' + '}', CGI.escape(sandbox_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(update_sandbox_template_request) + + # return_type + return_type = opts[:debug_return_type] || 'SandboxTemplate' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.update_sandbox", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:PATCH, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#update_sandbox\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Write a workspace file + # @param session_id [String] + # @param sandbox_file_write_request [SandboxFileWriteRequest] + # @param [Hash] opts the optional parameters + # @return [nil] + def write_sandbox_session_file(session_id, sandbox_file_write_request, opts = {}) + write_sandbox_session_file_with_http_info(session_id, sandbox_file_write_request, opts) + nil + end + + # Write a workspace file + # @param session_id [String] + # @param sandbox_file_write_request [SandboxFileWriteRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def write_sandbox_session_file_with_http_info(session_id, sandbox_file_write_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SandboxesApi.write_sandbox_session_file ...' + end + # verify the required parameter 'session_id' is set + if @api_client.config.client_side_validation && session_id.nil? + fail ArgumentError, "Missing the required parameter 'session_id' when calling SandboxesApi.write_sandbox_session_file" + end + # verify the required parameter 'sandbox_file_write_request' is set + if @api_client.config.client_side_validation && sandbox_file_write_request.nil? + fail ArgumentError, "Missing the required parameter 'sandbox_file_write_request' when calling SandboxesApi.write_sandbox_session_file" + end + # resource path + local_var_path = '/sandbox-sessions/{sessionId}/files/write'.sub('{' + 'sessionId' + '}', CGI.escape(session_id.to_s)) + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(sandbox_file_write_request) + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || ['ServiceRoleKey', 'AuthUserAccessToken', 'UserToken'] + + new_options = opts.merge( + :operation => :"SandboxesApi.write_sandbox_session_file", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SandboxesApi#write_sandbox_session_file\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + end +end diff --git a/lib/volcano/generated/lib/volcano-generated/api/service_keys_api.rb b/lib/volcano/generated/lib/volcano-generated/api/service_keys_api.rb index 8a816c56..7463f349 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/service_keys_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/service_keys_api.rb @@ -74,7 +74,7 @@ def create_service_key_with_http_info(id, create_service_key_request, opts = {}) return_type = opts[:debug_return_type] || 'ServiceKey' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ServiceKeysApi.create_service_key", @@ -141,7 +141,7 @@ def delete_service_key_with_http_info(id, key_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ServiceKeysApi.delete_service_key", @@ -210,7 +210,7 @@ def get_service_key_with_http_info(id, key_id, opts = {}) return_type = opts[:debug_return_type] || 'ServiceKey' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ServiceKeysApi.get_service_key", @@ -311,7 +311,7 @@ def list_service_keys_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedServiceKeys' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ServiceKeysApi.list_service_keys", @@ -331,7 +331,7 @@ def list_service_keys_with_http_info(id, opts = {}) end # Regenerate service key - # Generate new JWT value for existing key. The old key is immediately invalidated. Update your backend services with the new key before regenerating in production. + # Generate new JWT value for existing key. The old key stops working within a few seconds. Update your backend services with the new key before regenerating in production. # @param id [String] Project ID # @param key_id [String] # @param [Hash] opts the optional parameters @@ -342,7 +342,7 @@ def regenerate_service_key(id, key_id, opts = {}) end # Regenerate service key - # Generate new JWT value for existing key. The old key is immediately invalidated. Update your backend services with the new key before regenerating in production. + # Generate new JWT value for existing key. The old key stops working within a few seconds. Update your backend services with the new key before regenerating in production. # @param id [String] Project ID # @param key_id [String] # @param [Hash] opts the optional parameters @@ -380,7 +380,7 @@ def regenerate_service_key_with_http_info(id, key_id, opts = {}) return_type = opts[:debug_return_type] || 'ServiceKey' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"ServiceKeysApi.regenerate_service_key", diff --git a/lib/volcano/generated/lib/volcano-generated/api/storage_admin_api.rb b/lib/volcano/generated/lib/volcano-generated/api/storage_admin_api.rb index 8dd6d4e1..5f5b8ceb 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/storage_admin_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/storage_admin_api.rb @@ -63,7 +63,7 @@ def get_storage_stats_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'StorageStats' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StorageAdminApi.get_storage_stats", @@ -167,7 +167,7 @@ def list_storage_objects_admin_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ListStorageObjectsAdmin200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StorageAdminApi.list_storage_objects_admin", diff --git a/lib/volcano/generated/lib/volcano-generated/api/storage_buckets_api.rb b/lib/volcano/generated/lib/volcano-generated/api/storage_buckets_api.rb index fcf44d8f..dba91207 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/storage_buckets_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/storage_buckets_api.rb @@ -72,7 +72,7 @@ def create_storage_bucket_with_http_info(id, create_storage_bucket_request, opts return_type = opts[:debug_return_type] || 'StorageBucket' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StorageBucketsApi.create_storage_bucket", @@ -150,7 +150,7 @@ def delete_storage_bucket_with_http_info(id, bucket_name, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StorageBucketsApi.delete_storage_bucket", @@ -230,7 +230,7 @@ def get_storage_bucket_with_http_info(id, bucket_name, opts = {}) return_type = opts[:debug_return_type] || 'StorageBucket' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StorageBucketsApi.get_storage_bucket", @@ -324,7 +324,7 @@ def list_storage_buckets_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'ListStorageBuckets200Response' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StorageBucketsApi.list_storage_buckets", @@ -415,7 +415,7 @@ def update_storage_bucket_with_http_info(id, bucket_name, update_storage_bucket_ return_type = opts[:debug_return_type] || 'StorageBucket' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StorageBucketsApi.update_storage_bucket", diff --git a/lib/volcano/generated/lib/volcano-generated/api/storage_policies_api.rb b/lib/volcano/generated/lib/volcano-generated/api/storage_policies_api.rb index 014eb23a..5c53c752 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/storage_policies_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/storage_policies_api.rb @@ -91,7 +91,7 @@ def create_storage_policy_with_http_info(id, bucket_name, create_storage_policy_ return_type = opts[:debug_return_type] || 'StoragePolicy' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StoragePoliciesApi.create_storage_policy", @@ -175,7 +175,7 @@ def delete_storage_policy_with_http_info(id, bucket_name, policy_id, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StoragePoliciesApi.delete_storage_policy", @@ -255,7 +255,7 @@ def list_storage_policies_with_http_info(id, bucket_name, opts = {}) return_type = opts[:debug_return_type] || 'Array' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"StoragePoliciesApi.list_storage_policies", diff --git a/lib/volcano/generated/lib/volcano-generated/api/system_api.rb b/lib/volcano/generated/lib/volcano-generated/api/system_api.rb index 0f15388e..4c7cdcd1 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/system_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/system_api.rb @@ -19,6 +19,314 @@ class SystemApi def initialize(api_client = ApiClient.default) @api_client = api_client end + # Model Context Protocol endpoint + # Streamable-HTTP MCP endpoint: one JSON-RPC 2.0 object per request, one response per request. There is no server-to-client stream, so a `GET` returns `405`, and a batched array is rejected. Authenticated with a **project access token**. The endpoint takes its project from the credential, so a platform token is refused with `403` — it names no project, and letting a tool argument choose one would hand an agent its own blast radius. Scope carries over from the REST API. A `read_only` token is not offered mutating tools or credential-returning reads, and is refused if it calls one anyway. Revoking the token ends MCP access on the same path it ends API access. Methods: `initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call`. See the [MCP guide](https://docs.volcano.dev/platform/interfaces/mcp) for the tool surface and client configuration. + # @param call_mcp_request [CallMCPRequest] + # @param [Hash] opts the optional parameters + # @return [CallMCP200Response] + def call_mcp(call_mcp_request, opts = {}) + data, _status_code, _headers = call_mcp_with_http_info(call_mcp_request, opts) + data + end + + # Model Context Protocol endpoint + # Streamable-HTTP MCP endpoint: one JSON-RPC 2.0 object per request, one response per request. There is no server-to-client stream, so a `GET` returns `405`, and a batched array is rejected. Authenticated with a **project access token**. The endpoint takes its project from the credential, so a platform token is refused with `403` — it names no project, and letting a tool argument choose one would hand an agent its own blast radius. Scope carries over from the REST API. A `read_only` token is not offered mutating tools or credential-returning reads, and is refused if it calls one anyway. Revoking the token ends MCP access on the same path it ends API access. Methods: `initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call`. See the [MCP guide](https://docs.volcano.dev/platform/interfaces/mcp) for the tool surface and client configuration. + # @param call_mcp_request [CallMCPRequest] + # @param [Hash] opts the optional parameters + # @return [Array<(CallMCP200Response, Integer, Hash)>] CallMCP200Response data, response status code and response headers + def call_mcp_with_http_info(call_mcp_request, opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SystemApi.call_mcp ...' + end + # verify the required parameter 'call_mcp_request' is set + if @api_client.config.client_side_validation && call_mcp_request.nil? + fail ArgumentError, "Missing the required parameter 'call_mcp_request' when calling SystemApi.call_mcp" + end + # resource path + local_var_path = '/mcp' + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + # HTTP header 'Content-Type' + content_type = @api_client.select_header_content_type(['application/json']) + if !content_type.nil? + header_params['Content-Type'] = content_type + end + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] || @api_client.object_to_http_body(call_mcp_request) + + # return_type + return_type = opts[:debug_return_type] || 'CallMCP200Response' + + # auth_names + auth_names = opts[:debug_auth_names] || ['ProjectAccessToken'] + + new_options = opts.merge( + :operation => :"SystemApi.call_mcp", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SystemApi#call_mcp\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Fetch the OpenAPI specification as JSON + # Returns this specification as a self-contained JSON document, with every reference resolved. It is generated from the same document the server validates requests against, so a client generated from it cannot describe a different API than the one that answers. No credential is required: a client generator fetches this by URL before its user has a token, and every path here is already published in the API reference. The response carries a strong `ETag`; send it back as `If-None-Match` to get `304 Not Modified` instead of the whole document. + # @param [Hash] opts the optional parameters + # @option opts [String] :if_none_match Entity tag from an earlier response, returning `304 Not Modified` while it still matches. Accepts the full condition: `*`, a comma-separated list, and weak tags of the form `W/\"tag\"`. + # @return [Hash] + def get_open_api_spec_json(opts = {}) + data, _status_code, _headers = get_open_api_spec_json_with_http_info(opts) + data + end + + # Fetch the OpenAPI specification as JSON + # Returns this specification as a self-contained JSON document, with every reference resolved. It is generated from the same document the server validates requests against, so a client generated from it cannot describe a different API than the one that answers. No credential is required: a client generator fetches this by URL before its user has a token, and every path here is already published in the API reference. The response carries a strong `ETag`; send it back as `If-None-Match` to get `304 Not Modified` instead of the whole document. + # @param [Hash] opts the optional parameters + # @option opts [String] :if_none_match Entity tag from an earlier response, returning `304 Not Modified` while it still matches. Accepts the full condition: `*`, a comma-separated list, and weak tags of the form `W/\"tag\"`. + # @return [Array<(Hash, Integer, Hash)>] Hash data, response status code and response headers + def get_open_api_spec_json_with_http_info(opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SystemApi.get_open_api_spec_json ...' + end + # resource path + local_var_path = '/openapi.json' + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + header_params[:'If-None-Match'] = opts[:'if_none_match'] if !opts[:'if_none_match'].nil? + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'Hash' + + # auth_names + auth_names = opts[:debug_auth_names] || [] + + new_options = opts.merge( + :operation => :"SystemApi.get_open_api_spec_json", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SystemApi#get_open_api_spec_json\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Fetch the OpenAPI specification as YAML + # The same document as `/openapi.json`, serialized as YAML for tools that prefer it. See that operation for caching and authentication notes. + # @param [Hash] opts the optional parameters + # @option opts [String] :if_none_match Entity tag from an earlier response, returning `304 Not Modified` while it still matches. Accepts the full condition: `*`, a comma-separated list, and weak tags of the form `W/\"tag\"`. + # @return [Hash] + def get_open_api_spec_yaml(opts = {}) + data, _status_code, _headers = get_open_api_spec_yaml_with_http_info(opts) + data + end + + # Fetch the OpenAPI specification as YAML + # The same document as `/openapi.json`, serialized as YAML for tools that prefer it. See that operation for caching and authentication notes. + # @param [Hash] opts the optional parameters + # @option opts [String] :if_none_match Entity tag from an earlier response, returning `304 Not Modified` while it still matches. Accepts the full condition: `*`, a comma-separated list, and weak tags of the form `W/\"tag\"`. + # @return [Array<(Hash, Integer, Hash)>] Hash data, response status code and response headers + def get_open_api_spec_yaml_with_http_info(opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SystemApi.get_open_api_spec_yaml ...' + end + # resource path + local_var_path = '/openapi.yaml' + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/yaml', 'application/json']) unless header_params['Accept'] + header_params[:'If-None-Match'] = opts[:'if_none_match'] if !opts[:'if_none_match'].nil? + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] || 'Hash' + + # auth_names + auth_names = opts[:debug_auth_names] || [] + + new_options = opts.merge( + :operation => :"SystemApi.get_open_api_spec_yaml", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SystemApi#get_open_api_spec_yaml\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Check the JSON OpenAPI specification + # The headers `GET /openapi.json` would return, so a cache can pick up the current `ETag` without transferring the document. + # @param [Hash] opts the optional parameters + # @option opts [String] :if_none_match Entity tag from an earlier response, returning `304 Not Modified` while it still matches. Accepts the full condition: `*`, a comma-separated list, and weak tags of the form `W/\"tag\"`. + # @return [nil] + def head_open_api_spec_json(opts = {}) + head_open_api_spec_json_with_http_info(opts) + nil + end + + # Check the JSON OpenAPI specification + # The headers `GET /openapi.json` would return, so a cache can pick up the current `ETag` without transferring the document. + # @param [Hash] opts the optional parameters + # @option opts [String] :if_none_match Entity tag from an earlier response, returning `304 Not Modified` while it still matches. Accepts the full condition: `*`, a comma-separated list, and weak tags of the form `W/\"tag\"`. + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def head_open_api_spec_json_with_http_info(opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SystemApi.head_open_api_spec_json ...' + end + # resource path + local_var_path = '/openapi.json' + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + header_params[:'If-None-Match'] = opts[:'if_none_match'] if !opts[:'if_none_match'].nil? + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || [] + + new_options = opts.merge( + :operation => :"SystemApi.head_open_api_spec_json", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:HEAD, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SystemApi#head_open_api_spec_json\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + + # Check the YAML OpenAPI specification + # The headers `GET /openapi.yaml` would return, so a cache can pick up the current `ETag` without transferring the document. + # @param [Hash] opts the optional parameters + # @option opts [String] :if_none_match Entity tag from an earlier response, returning `304 Not Modified` while it still matches. Accepts the full condition: `*`, a comma-separated list, and weak tags of the form `W/\"tag\"`. + # @return [nil] + def head_open_api_spec_yaml(opts = {}) + head_open_api_spec_yaml_with_http_info(opts) + nil + end + + # Check the YAML OpenAPI specification + # The headers `GET /openapi.yaml` would return, so a cache can pick up the current `ETag` without transferring the document. + # @param [Hash] opts the optional parameters + # @option opts [String] :if_none_match Entity tag from an earlier response, returning `304 Not Modified` while it still matches. Accepts the full condition: `*`, a comma-separated list, and weak tags of the form `W/\"tag\"`. + # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers + def head_open_api_spec_yaml_with_http_info(opts = {}) + if @api_client.config.debugging + @api_client.config.logger.debug 'Calling API: SystemApi.head_open_api_spec_yaml ...' + end + # resource path + local_var_path = '/openapi.yaml' + + # query parameters + query_params = opts[:query_params] || {} + + # header parameters + header_params = opts[:header_params] || {} + # HTTP header 'Accept' (if needed) + header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept'] + header_params[:'If-None-Match'] = opts[:'if_none_match'] if !opts[:'if_none_match'].nil? + + # form parameters + form_params = opts[:form_params] || {} + + # http body (model) + post_body = opts[:debug_body] + + # return_type + return_type = opts[:debug_return_type] + + # auth_names + auth_names = opts[:debug_auth_names] || [] + + new_options = opts.merge( + :operation => :"SystemApi.head_open_api_spec_yaml", + :header_params => header_params, + :query_params => query_params, + :form_params => form_params, + :body => post_body, + :auth_names => auth_names, + :return_type => return_type + ) + + data, status_code, headers = @api_client.call_api(:HEAD, local_var_path, new_options) + if @api_client.config.debugging + @api_client.config.logger.debug "API called: SystemApi#head_open_api_spec_yaml\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}" + end + return data, status_code, headers + end + # Health check endpoint # Returns server health status. Used for load balancer and monitoring checks. # @param [Hash] opts the optional parameters diff --git a/lib/volcano/generated/lib/volcano-generated/api/variables_api.rb b/lib/volcano/generated/lib/volcano-generated/api/variables_api.rb index 82522d03..5cec09d2 100644 --- a/lib/volcano/generated/lib/volcano-generated/api/variables_api.rb +++ b/lib/volcano/generated/lib/volcano-generated/api/variables_api.rb @@ -74,7 +74,7 @@ def create_variable_with_http_info(id, create_variable_request, opts = {}) return_type = opts[:debug_return_type] || 'Variable' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"VariablesApi.create_variable", @@ -156,7 +156,7 @@ def delete_variable_with_http_info(id, name, opts = {}) return_type = opts[:debug_return_type] # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"VariablesApi.delete_variable", @@ -238,7 +238,7 @@ def get_variable_with_http_info(id, name, opts = {}) return_type = opts[:debug_return_type] || 'Variable' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"VariablesApi.get_variable", @@ -339,7 +339,7 @@ def list_variables_with_http_info(id, opts = {}) return_type = opts[:debug_return_type] || 'PaginatedVariables' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"VariablesApi.list_variables", @@ -432,7 +432,7 @@ def update_variable_with_http_info(id, name, update_variable_request, opts = {}) return_type = opts[:debug_return_type] || 'Variable' # auth_names - auth_names = opts[:debug_auth_names] || ['UserToken'] + auth_names = opts[:debug_auth_names] || ['UserToken', 'ProjectAccessToken'] new_options = opts.merge( :operation => :"VariablesApi.update_variable", diff --git a/lib/volcano/generated/lib/volcano-generated/configuration.rb b/lib/volcano/generated/lib/volcano-generated/configuration.rb index 0c605526..efb22e7b 100644 --- a/lib/volcano/generated/lib/volcano-generated/configuration.rb +++ b/lib/volcano/generated/lib/volcano-generated/configuration.rb @@ -272,7 +272,15 @@ def auth_settings { type: 'bearer', in: 'header', - format: 'JWT', + format: 'opaque', + key: 'Authorization', + value: "Bearer #{access_token_with_refresh}" + }, + 'ProjectAccessToken' => + { + type: 'bearer', + in: 'header', + format: 'opaque', key: 'Authorization', value: "Bearer #{access_token_with_refresh}" }, diff --git a/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response.rb b/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response.rb new file mode 100644 index 00000000..307ea872 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response.rb @@ -0,0 +1,220 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class CallMCP200Response < ApiModelBase + attr_accessor :jsonrpc + + attr_accessor :id + + attr_accessor :result + + attr_accessor :error + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'jsonrpc' => :'jsonrpc', + :'id' => :'id', + :'result' => :'result', + :'error' => :'error' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'jsonrpc' => :'String', + :'id' => :'CallMCP200ResponseId', + :'result' => :'Hash', + :'error' => :'CallMCP200ResponseError' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + :'id', + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::CallMCP200Response` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::CallMCP200Response`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'jsonrpc') + self.jsonrpc = attributes[:'jsonrpc'] + else + self.jsonrpc = nil + end + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'result') + if (value = attributes[:'result']).is_a?(Hash) + self.result = value + end + end + + if attributes.key?(:'error') + self.error = attributes[:'error'] + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @jsonrpc.nil? + invalid_properties.push('invalid value for "jsonrpc", jsonrpc cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @jsonrpc.nil? + jsonrpc_validator = EnumAttributeValidator.new('String', ["2.0"]) + return false unless jsonrpc_validator.valid?(@jsonrpc) + true + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] jsonrpc Object to be assigned + def jsonrpc=(jsonrpc) + validator = EnumAttributeValidator.new('String', ["2.0"]) + unless validator.valid?(jsonrpc) + fail ArgumentError, "invalid value for \"jsonrpc\", must be one of #{validator.allowable_values}." + end + @jsonrpc = jsonrpc + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + jsonrpc == o.jsonrpc && + id == o.id && + result == o.result && + error == o.error + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [jsonrpc, id, result, error].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response_error.rb b/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response_error.rb new file mode 100644 index 00000000..cd5354b5 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response_error.rb @@ -0,0 +1,190 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class CallMCP200ResponseError < ApiModelBase + attr_accessor :code + + attr_accessor :message + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'code' => :'code', + :'message' => :'message' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'code' => :'Integer', + :'message' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::CallMCP200ResponseError` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::CallMCP200ResponseError`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'code') + self.code = attributes[:'code'] + else + self.code = nil + end + + if attributes.key?(:'message') + self.message = attributes[:'message'] + else + self.message = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @code.nil? + invalid_properties.push('invalid value for "code", code cannot be nil.') + end + + if @message.nil? + invalid_properties.push('invalid value for "message", message cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @code.nil? + return false if @message.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] code Value to be assigned + def code=(code) + if code.nil? + fail ArgumentError, 'code cannot be nil' + end + + @code = code + end + + # Custom attribute writer method with validation + # @param [Object] message Value to be assigned + def message=(message) + if message.nil? + fail ArgumentError, 'message cannot be nil' + end + + @message = message + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + code == o.code && + message == o.message + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [code, message].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response_id.rb b/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response_id.rb new file mode 100644 index 00000000..d008fbf5 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/call_mcp200_response_id.rb @@ -0,0 +1,105 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + # Echoes the request's id. Null when the request could not be read well enough to determine one. + module CallMCP200ResponseId + class << self + # List of class defined in oneOf (OpenAPI v3) + def openapi_one_of + [ + :'Integer', + :'String' + ] + end + + # Builds the object + # @param [Mixed] Data to be matched against the list of oneOf items + # @return [Object] Returns the model or the data itself + def build(data) + # Go through the list of oneOf items and attempt to identify the appropriate one. + # Note: + # - We do not attempt to check whether exactly one item matches. + # - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 }) + # due to the way the deserialization is made in the base_object template (it just casts without verifying). + # - TODO: scalar values are de facto behaving as if they were nullable. + # - TODO: logging when debugging is set. + openapi_one_of.each do |klass| + begin + next if klass == :AnyType # "nullable: true" + return find_and_cast_into_type(klass, data) + rescue # rescue all errors so we keep iterating even if the current item lookup raises + end + end + + openapi_one_of.include?(:AnyType) ? data : nil + end + + private + + SchemaMismatchError = Class.new(StandardError) + + # Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse. + def find_and_cast_into_type(klass, data) + return if data.nil? + + case klass.to_s + when 'Boolean' + return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass) + when 'Float' + return data if data.instance_of?(Float) + when 'Integer' + return data if data.instance_of?(Integer) + when 'Time' + return Time.parse(data) + when 'Date' + return Date.iso8601(data) + when 'String' + return data if data.instance_of?(String) + when 'Object' # "type: object" + return data if data.instance_of?(Hash) + when /\AArray<(?.+)>\z/ # "type: array" + if data.instance_of?(Array) + sub_type = Regexp.last_match[:sub_type] + return data.map { |item| find_and_cast_into_type(sub_type, item) } + end + when /\AHash.+)>\z/ # "type: object" with "additionalProperties: { ... }" + if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) } + sub_type = Regexp.last_match[:sub_type] + return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) } + end + else # model + const = Object.const_get('Volcano::Generated').const_get(klass) + if const + if const.respond_to?(:openapi_one_of) # nested oneOf model + model = const.build(data) + return model unless model.nil? + else + # raise if data contains keys that are not known to the model + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? + model = const.build_from_hash(data) + return model if model + end + end + end + + raise # if no match by now, raise + rescue + raise SchemaMismatchError, "#{data} doesn't match the #{klass} type" + end + end + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/call_mcp_request.rb b/lib/volcano/generated/lib/volcano-generated/models/call_mcp_request.rb new file mode 100644 index 00000000..77e264b7 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/call_mcp_request.rb @@ -0,0 +1,236 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + # A JSON-RPC 2.0 request object. + class CallMCPRequest < ApiModelBase + attr_accessor :jsonrpc + + # The MCP method to call. + attr_accessor :method + + attr_accessor :id + + attr_accessor :params + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'jsonrpc' => :'jsonrpc', + :'method' => :'method', + :'id' => :'id', + :'params' => :'params' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'jsonrpc' => :'String', + :'method' => :'String', + :'id' => :'CallMCPRequestId', + :'params' => :'Hash' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::CallMCPRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::CallMCPRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'jsonrpc') + self.jsonrpc = attributes[:'jsonrpc'] + else + self.jsonrpc = nil + end + + if attributes.key?(:'method') + self.method = attributes[:'method'] + else + self.method = nil + end + + if attributes.key?(:'id') + self.id = attributes[:'id'] + end + + if attributes.key?(:'params') + if (value = attributes[:'params']).is_a?(Hash) + self.params = value + end + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @jsonrpc.nil? + invalid_properties.push('invalid value for "jsonrpc", jsonrpc cannot be nil.') + end + + if @method.nil? + invalid_properties.push('invalid value for "method", method cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @jsonrpc.nil? + jsonrpc_validator = EnumAttributeValidator.new('String', ["2.0"]) + return false unless jsonrpc_validator.valid?(@jsonrpc) + return false if @method.nil? + true + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] jsonrpc Object to be assigned + def jsonrpc=(jsonrpc) + validator = EnumAttributeValidator.new('String', ["2.0"]) + unless validator.valid?(jsonrpc) + fail ArgumentError, "invalid value for \"jsonrpc\", must be one of #{validator.allowable_values}." + end + @jsonrpc = jsonrpc + end + + # Custom attribute writer method with validation + # @param [Object] method Value to be assigned + def method=(method) + if method.nil? + fail ArgumentError, 'method cannot be nil' + end + + @method = method + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + jsonrpc == o.jsonrpc && + method == o.method && + id == o.id && + params == o.params + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [jsonrpc, method, id, params].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/call_mcp_request_id.rb b/lib/volcano/generated/lib/volcano-generated/models/call_mcp_request_id.rb new file mode 100644 index 00000000..57885ad7 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/call_mcp_request_id.rb @@ -0,0 +1,105 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + # Request identifier, echoed verbatim. Omit it to send a notification, which is answered with `202` and no body. + module CallMCPRequestId + class << self + # List of class defined in oneOf (OpenAPI v3) + def openapi_one_of + [ + :'Integer', + :'String' + ] + end + + # Builds the object + # @param [Mixed] Data to be matched against the list of oneOf items + # @return [Object] Returns the model or the data itself + def build(data) + # Go through the list of oneOf items and attempt to identify the appropriate one. + # Note: + # - We do not attempt to check whether exactly one item matches. + # - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 }) + # due to the way the deserialization is made in the base_object template (it just casts without verifying). + # - TODO: scalar values are de facto behaving as if they were nullable. + # - TODO: logging when debugging is set. + openapi_one_of.each do |klass| + begin + next if klass == :AnyType # "nullable: true" + return find_and_cast_into_type(klass, data) + rescue # rescue all errors so we keep iterating even if the current item lookup raises + end + end + + openapi_one_of.include?(:AnyType) ? data : nil + end + + private + + SchemaMismatchError = Class.new(StandardError) + + # Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse. + def find_and_cast_into_type(klass, data) + return if data.nil? + + case klass.to_s + when 'Boolean' + return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass) + when 'Float' + return data if data.instance_of?(Float) + when 'Integer' + return data if data.instance_of?(Integer) + when 'Time' + return Time.parse(data) + when 'Date' + return Date.iso8601(data) + when 'String' + return data if data.instance_of?(String) + when 'Object' # "type: object" + return data if data.instance_of?(Hash) + when /\AArray<(?.+)>\z/ # "type: array" + if data.instance_of?(Array) + sub_type = Regexp.last_match[:sub_type] + return data.map { |item| find_and_cast_into_type(sub_type, item) } + end + when /\AHash.+)>\z/ # "type: object" with "additionalProperties: { ... }" + if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) } + sub_type = Regexp.last_match[:sub_type] + return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) } + end + else # model + const = Object.const_get('Volcano::Generated').const_get(klass) + if const + if const.respond_to?(:openapi_one_of) # nested oneOf model + model = const.build(data) + return model unless model.nil? + else + # raise if data contains keys that are not known to the model + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? + model = const.build_from_hash(data) + return model if model + end + end + end + + raise # if no match by now, raise + rescue + raise SchemaMismatchError, "#{data} doesn't match the #{klass} type" + end + end + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/create_frontend_function_route_request.rb b/lib/volcano/generated/lib/volcano-generated/models/create_frontend_function_route_request.rb new file mode 100644 index 00000000..68b531f0 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/create_frontend_function_route_request.rb @@ -0,0 +1,230 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class CreateFrontendFunctionRouteRequest < ApiModelBase + attr_accessor :function_id + + attr_accessor :path_prefix + + attr_accessor :strip_prefix + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'function_id' => :'function_id', + :'path_prefix' => :'path_prefix', + :'strip_prefix' => :'strip_prefix' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'function_id' => :'String', + :'path_prefix' => :'String', + :'strip_prefix' => :'Boolean' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::CreateFrontendFunctionRouteRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::CreateFrontendFunctionRouteRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'function_id') + self.function_id = attributes[:'function_id'] + else + self.function_id = nil + end + + if attributes.key?(:'path_prefix') + self.path_prefix = attributes[:'path_prefix'] + else + self.path_prefix = nil + end + + if attributes.key?(:'strip_prefix') + self.strip_prefix = attributes[:'strip_prefix'] + else + self.strip_prefix = false + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @function_id.nil? + invalid_properties.push('invalid value for "function_id", function_id cannot be nil.') + end + + if @path_prefix.nil? + invalid_properties.push('invalid value for "path_prefix", path_prefix cannot be nil.') + end + + if @path_prefix.to_s.length > 512 + invalid_properties.push('invalid value for "path_prefix", the character length must be smaller than or equal to 512.') + end + + if @path_prefix.to_s.length < 2 + invalid_properties.push('invalid value for "path_prefix", the character length must be greater than or equal to 2.') + end + + pattern = Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + if @path_prefix !~ pattern + invalid_properties.push("invalid value for \"path_prefix\", must conform to the pattern #{pattern}.") + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @function_id.nil? + return false if @path_prefix.nil? + return false if @path_prefix.to_s.length > 512 + return false if @path_prefix.to_s.length < 2 + return false if @path_prefix !~ Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + true + end + + # Custom attribute writer method with validation + # @param [Object] function_id Value to be assigned + def function_id=(function_id) + if function_id.nil? + fail ArgumentError, 'function_id cannot be nil' + end + + @function_id = function_id + end + + # Custom attribute writer method with validation + # @param [Object] path_prefix Value to be assigned + def path_prefix=(path_prefix) + if path_prefix.nil? + fail ArgumentError, 'path_prefix cannot be nil' + end + + if path_prefix.to_s.length > 512 + fail ArgumentError, 'invalid value for "path_prefix", the character length must be smaller than or equal to 512.' + end + + if path_prefix.to_s.length < 2 + fail ArgumentError, 'invalid value for "path_prefix", the character length must be greater than or equal to 2.' + end + + pattern = Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + if path_prefix !~ pattern + fail ArgumentError, "invalid value for \"path_prefix\", must conform to the pattern #{pattern}." + end + + @path_prefix = path_prefix + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + function_id == o.function_id && + path_prefix == o.path_prefix && + strip_prefix == o.strip_prefix + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [function_id, path_prefix, strip_prefix].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/create_project_access_token_request.rb b/lib/volcano/generated/lib/volcano-generated/models/create_project_access_token_request.rb new file mode 100644 index 00000000..677846df --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/create_project_access_token_request.rb @@ -0,0 +1,241 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class CreateProjectAccessTokenRequest < ApiModelBase + # Held by any of the project's tokens you have not revoked, including one that has expired. Creating a duplicate returns 409 with code `access_token_name_exists`; revoking the holder frees the name, so a rotation can keep the name its caller already references. + attr_accessor :name + + attr_accessor :scope + + # Omit for a token that does not expire. + attr_accessor :expires_at + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'name' => :'name', + :'scope' => :'scope', + :'expires_at' => :'expires_at' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'name' => :'String', + :'scope' => :'ProjectAccessTokenScope', + :'expires_at' => :'Time' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::CreateProjectAccessTokenRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::CreateProjectAccessTokenRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'name') + self.name = attributes[:'name'] + else + self.name = nil + end + + if attributes.key?(:'scope') + self.scope = attributes[:'scope'] + else + self.scope = nil + end + + if attributes.key?(:'expires_at') + self.expires_at = attributes[:'expires_at'] + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @name.nil? + invalid_properties.push('invalid value for "name", name cannot be nil.') + end + + if @name.to_s.length > 100 + invalid_properties.push('invalid value for "name", the character length must be smaller than or equal to 100.') + end + + if @name.to_s.length < 1 + invalid_properties.push('invalid value for "name", the character length must be greater than or equal to 1.') + end + + if @scope.nil? + invalid_properties.push('invalid value for "scope", scope cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @name.nil? + return false if @name.to_s.length > 100 + return false if @name.to_s.length < 1 + return false if @scope.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] name Value to be assigned + def name=(name) + if name.nil? + fail ArgumentError, 'name cannot be nil' + end + + if name.to_s.length > 100 + fail ArgumentError, 'invalid value for "name", the character length must be smaller than or equal to 100.' + end + + if name.to_s.length < 1 + fail ArgumentError, 'invalid value for "name", the character length must be greater than or equal to 1.' + end + + @name = name + end + + # Custom attribute writer method with validation + # @param [Object] scope Value to be assigned + def scope=(scope) + if scope.nil? + fail ArgumentError, 'scope cannot be nil' + end + + @scope = scope + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + name == o.name && + scope == o.scope && + expires_at == o.expires_at + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [name, scope, expires_at].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/create_sandbox_session_request.rb b/lib/volcano/generated/lib/volcano-generated/models/create_sandbox_session_request.rb new file mode 100644 index 00000000..46b246bd --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/create_sandbox_session_request.rb @@ -0,0 +1,304 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class CreateSandboxSessionRequest < ApiModelBase + attr_accessor :preset + + attr_accessor :sandbox_id + + attr_accessor :memory_mb + + attr_accessor :region + + attr_accessor :max_duration_seconds + + attr_accessor :idle_timeout_seconds + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'preset' => :'preset', + :'sandbox_id' => :'sandbox_id', + :'memory_mb' => :'memory_mb', + :'region' => :'region', + :'max_duration_seconds' => :'max_duration_seconds', + :'idle_timeout_seconds' => :'idle_timeout_seconds' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'preset' => :'String', + :'sandbox_id' => :'String', + :'memory_mb' => :'Integer', + :'region' => :'String', + :'max_duration_seconds' => :'Integer', + :'idle_timeout_seconds' => :'Integer' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::CreateSandboxSessionRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::CreateSandboxSessionRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'preset') + self.preset = attributes[:'preset'] + end + + if attributes.key?(:'sandbox_id') + self.sandbox_id = attributes[:'sandbox_id'] + end + + if attributes.key?(:'memory_mb') + self.memory_mb = attributes[:'memory_mb'] + end + + if attributes.key?(:'region') + self.region = attributes[:'region'] + else + self.region = nil + end + + if attributes.key?(:'max_duration_seconds') + self.max_duration_seconds = attributes[:'max_duration_seconds'] + else + self.max_duration_seconds = 3600 + end + + if attributes.key?(:'idle_timeout_seconds') + self.idle_timeout_seconds = attributes[:'idle_timeout_seconds'] + else + self.idle_timeout_seconds = 0 + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @region.nil? + invalid_properties.push('invalid value for "region", region cannot be nil.') + end + + pattern = Regexp.new(/^aws-[a-z0-9-]+$/) + if @region !~ pattern + invalid_properties.push("invalid value for \"region\", must conform to the pattern #{pattern}.") + end + + if !@max_duration_seconds.nil? && @max_duration_seconds > 28800 + invalid_properties.push('invalid value for "max_duration_seconds", must be smaller than or equal to 28800.') + end + + if !@max_duration_seconds.nil? && @max_duration_seconds < 30 + invalid_properties.push('invalid value for "max_duration_seconds", must be greater than or equal to 30.') + end + + if !@idle_timeout_seconds.nil? && @idle_timeout_seconds > 28800 + invalid_properties.push('invalid value for "idle_timeout_seconds", must be smaller than or equal to 28800.') + end + + if !@idle_timeout_seconds.nil? && @idle_timeout_seconds < 0 + invalid_properties.push('invalid value for "idle_timeout_seconds", must be greater than or equal to 0.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + preset_validator = EnumAttributeValidator.new('String', ["python3.12", "node22"]) + return false unless preset_validator.valid?(@preset) + memory_mb_validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + return false unless memory_mb_validator.valid?(@memory_mb) + return false if @region.nil? + return false if @region !~ Regexp.new(/^aws-[a-z0-9-]+$/) + return false if !@max_duration_seconds.nil? && @max_duration_seconds > 28800 + return false if !@max_duration_seconds.nil? && @max_duration_seconds < 30 + return false if !@idle_timeout_seconds.nil? && @idle_timeout_seconds > 28800 + return false if !@idle_timeout_seconds.nil? && @idle_timeout_seconds < 0 + true + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] preset Object to be assigned + def preset=(preset) + validator = EnumAttributeValidator.new('String', ["python3.12", "node22"]) + unless validator.valid?(preset) + fail ArgumentError, "invalid value for \"preset\", must be one of #{validator.allowable_values}." + end + @preset = preset + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] memory_mb Object to be assigned + def memory_mb=(memory_mb) + validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + unless validator.valid?(memory_mb) + fail ArgumentError, "invalid value for \"memory_mb\", must be one of #{validator.allowable_values}." + end + @memory_mb = memory_mb + end + + # Custom attribute writer method with validation + # @param [Object] region Value to be assigned + def region=(region) + if region.nil? + fail ArgumentError, 'region cannot be nil' + end + + pattern = Regexp.new(/^aws-[a-z0-9-]+$/) + if region !~ pattern + fail ArgumentError, "invalid value for \"region\", must conform to the pattern #{pattern}." + end + + @region = region + end + + # Custom attribute writer method with validation + # @param [Object] max_duration_seconds Value to be assigned + def max_duration_seconds=(max_duration_seconds) + if max_duration_seconds.nil? + fail ArgumentError, 'max_duration_seconds cannot be nil' + end + + if max_duration_seconds > 28800 + fail ArgumentError, 'invalid value for "max_duration_seconds", must be smaller than or equal to 28800.' + end + + if max_duration_seconds < 30 + fail ArgumentError, 'invalid value for "max_duration_seconds", must be greater than or equal to 30.' + end + + @max_duration_seconds = max_duration_seconds + end + + # Custom attribute writer method with validation + # @param [Object] idle_timeout_seconds Value to be assigned + def idle_timeout_seconds=(idle_timeout_seconds) + if idle_timeout_seconds.nil? + fail ArgumentError, 'idle_timeout_seconds cannot be nil' + end + + if idle_timeout_seconds > 28800 + fail ArgumentError, 'invalid value for "idle_timeout_seconds", must be smaller than or equal to 28800.' + end + + if idle_timeout_seconds < 0 + fail ArgumentError, 'invalid value for "idle_timeout_seconds", must be greater than or equal to 0.' + end + + @idle_timeout_seconds = idle_timeout_seconds + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + preset == o.preset && + sandbox_id == o.sandbox_id && + memory_mb == o.memory_mb && + region == o.region && + max_duration_seconds == o.max_duration_seconds && + idle_timeout_seconds == o.idle_timeout_seconds + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [preset, sandbox_id, memory_mb, region, max_duration_seconds, idle_timeout_seconds].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/create_sandbox_template_request.rb b/lib/volcano/generated/lib/volcano-generated/models/create_sandbox_template_request.rb new file mode 100644 index 00000000..674cf1ba --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/create_sandbox_template_request.rb @@ -0,0 +1,248 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class CreateSandboxTemplateRequest < ApiModelBase + attr_accessor :name + + attr_accessor :preset + + attr_accessor :memory_mb + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'name' => :'name', + :'preset' => :'preset', + :'memory_mb' => :'memory_mb' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'name' => :'String', + :'preset' => :'String', + :'memory_mb' => :'Integer' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::CreateSandboxTemplateRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::CreateSandboxTemplateRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'name') + self.name = attributes[:'name'] + else + self.name = nil + end + + if attributes.key?(:'preset') + self.preset = attributes[:'preset'] + else + self.preset = nil + end + + if attributes.key?(:'memory_mb') + self.memory_mb = attributes[:'memory_mb'] + else + self.memory_mb = MEMORY_MB::N1024 + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @name.nil? + invalid_properties.push('invalid value for "name", name cannot be nil.') + end + + pattern = Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + if @name !~ pattern + invalid_properties.push("invalid value for \"name\", must conform to the pattern #{pattern}.") + end + + if @preset.nil? + invalid_properties.push('invalid value for "preset", preset cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @name.nil? + return false if @name !~ Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + return false if @preset.nil? + preset_validator = EnumAttributeValidator.new('String', ["python3.12", "node22"]) + return false unless preset_validator.valid?(@preset) + memory_mb_validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + return false unless memory_mb_validator.valid?(@memory_mb) + true + end + + # Custom attribute writer method with validation + # @param [Object] name Value to be assigned + def name=(name) + if name.nil? + fail ArgumentError, 'name cannot be nil' + end + + pattern = Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + if name !~ pattern + fail ArgumentError, "invalid value for \"name\", must conform to the pattern #{pattern}." + end + + @name = name + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] preset Object to be assigned + def preset=(preset) + validator = EnumAttributeValidator.new('String', ["python3.12", "node22"]) + unless validator.valid?(preset) + fail ArgumentError, "invalid value for \"preset\", must be one of #{validator.allowable_values}." + end + @preset = preset + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] memory_mb Object to be assigned + def memory_mb=(memory_mb) + validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + unless validator.valid?(memory_mb) + fail ArgumentError, "invalid value for \"memory_mb\", must be one of #{validator.allowable_values}." + end + @memory_mb = memory_mb + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + name == o.name && + preset == o.preset && + memory_mb == o.memory_mb + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [name, preset, memory_mb].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/created_project_access_token.rb b/lib/volcano/generated/lib/volcano-generated/models/created_project_access_token.rb new file mode 100644 index 00000000..13e3cae1 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/created_project_access_token.rb @@ -0,0 +1,459 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class CreatedProjectAccessToken < ApiModelBase + attr_accessor :id + + attr_accessor :project_id + + # Unique per project. + attr_accessor :name + + # First 12 characters of the secret, for recognising a token in a list. + attr_accessor :token_prefix + + attr_accessor :scope + + # `revoked` means the token was deliberately revoked, by you or by the deletion of its project. `expired` means it simply reached `expires_at`; nothing was taken away. Both are refused, and both keep their record so a token's name, prefix, last use, and request history remain available after a leak. A token revoked before its expiry passed stays `revoked`, because that is the fact worth keeping. + attr_accessor :status + + # What created the token. + attr_accessor :token_source + + # Absent for a token that does not expire. + attr_accessor :expires_at + + # Updated at most once every few minutes, so it may lag slightly. + attr_accessor :last_used_at + + attr_accessor :created_at + + # Requests authenticated with this token since it was created. + attr_accessor :all_time_requests + + # The secret. Returned only here, and not recoverable afterwards: the server stores a hash rather than the value. Save it now. + attr_accessor :token + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'id' => :'id', + :'project_id' => :'project_id', + :'name' => :'name', + :'token_prefix' => :'token_prefix', + :'scope' => :'scope', + :'status' => :'status', + :'token_source' => :'token_source', + :'expires_at' => :'expires_at', + :'last_used_at' => :'last_used_at', + :'created_at' => :'created_at', + :'all_time_requests' => :'all_time_requests', + :'token' => :'token' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'id' => :'String', + :'project_id' => :'String', + :'name' => :'String', + :'token_prefix' => :'String', + :'scope' => :'ProjectAccessTokenScope', + :'status' => :'String', + :'token_source' => :'String', + :'expires_at' => :'Time', + :'last_used_at' => :'Time', + :'created_at' => :'Time', + :'all_time_requests' => :'Integer', + :'token' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + :'expires_at', + :'last_used_at', + ]) + end + + # List of class defined in allOf (OpenAPI v3) + def self.openapi_all_of + [ + :'ProjectAccessToken' + ] + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::CreatedProjectAccessToken` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::CreatedProjectAccessToken`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'project_id') + self.project_id = attributes[:'project_id'] + else + self.project_id = nil + end + + if attributes.key?(:'name') + self.name = attributes[:'name'] + else + self.name = nil + end + + if attributes.key?(:'token_prefix') + self.token_prefix = attributes[:'token_prefix'] + else + self.token_prefix = nil + end + + if attributes.key?(:'scope') + self.scope = attributes[:'scope'] + else + self.scope = nil + end + + if attributes.key?(:'status') + self.status = attributes[:'status'] + else + self.status = nil + end + + if attributes.key?(:'token_source') + self.token_source = attributes[:'token_source'] + else + self.token_source = nil + end + + if attributes.key?(:'expires_at') + self.expires_at = attributes[:'expires_at'] + end + + if attributes.key?(:'last_used_at') + self.last_used_at = attributes[:'last_used_at'] + end + + if attributes.key?(:'created_at') + self.created_at = attributes[:'created_at'] + else + self.created_at = nil + end + + if attributes.key?(:'all_time_requests') + self.all_time_requests = attributes[:'all_time_requests'] + else + self.all_time_requests = nil + end + + if attributes.key?(:'token') + self.token = attributes[:'token'] + else + self.token = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @id.nil? + invalid_properties.push('invalid value for "id", id cannot be nil.') + end + + if @project_id.nil? + invalid_properties.push('invalid value for "project_id", project_id cannot be nil.') + end + + if @name.nil? + invalid_properties.push('invalid value for "name", name cannot be nil.') + end + + if @token_prefix.nil? + invalid_properties.push('invalid value for "token_prefix", token_prefix cannot be nil.') + end + + if @scope.nil? + invalid_properties.push('invalid value for "scope", scope cannot be nil.') + end + + if @status.nil? + invalid_properties.push('invalid value for "status", status cannot be nil.') + end + + if @token_source.nil? + invalid_properties.push('invalid value for "token_source", token_source cannot be nil.') + end + + if @created_at.nil? + invalid_properties.push('invalid value for "created_at", created_at cannot be nil.') + end + + if @all_time_requests.nil? + invalid_properties.push('invalid value for "all_time_requests", all_time_requests cannot be nil.') + end + + if @token.nil? + invalid_properties.push('invalid value for "token", token cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @id.nil? + return false if @project_id.nil? + return false if @name.nil? + return false if @token_prefix.nil? + return false if @scope.nil? + return false if @status.nil? + status_validator = EnumAttributeValidator.new('String', ["active", "revoked", "expired"]) + return false unless status_validator.valid?(@status) + return false if @token_source.nil? + token_source_validator = EnumAttributeValidator.new('String', ["api", "cli", "dashboard"]) + return false unless token_source_validator.valid?(@token_source) + return false if @created_at.nil? + return false if @all_time_requests.nil? + return false if @token.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] id Value to be assigned + def id=(id) + if id.nil? + fail ArgumentError, 'id cannot be nil' + end + + @id = id + end + + # Custom attribute writer method with validation + # @param [Object] project_id Value to be assigned + def project_id=(project_id) + if project_id.nil? + fail ArgumentError, 'project_id cannot be nil' + end + + @project_id = project_id + end + + # Custom attribute writer method with validation + # @param [Object] name Value to be assigned + def name=(name) + if name.nil? + fail ArgumentError, 'name cannot be nil' + end + + @name = name + end + + # Custom attribute writer method with validation + # @param [Object] token_prefix Value to be assigned + def token_prefix=(token_prefix) + if token_prefix.nil? + fail ArgumentError, 'token_prefix cannot be nil' + end + + @token_prefix = token_prefix + end + + # Custom attribute writer method with validation + # @param [Object] scope Value to be assigned + def scope=(scope) + if scope.nil? + fail ArgumentError, 'scope cannot be nil' + end + + @scope = scope + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] status Object to be assigned + def status=(status) + validator = EnumAttributeValidator.new('String', ["active", "revoked", "expired"]) + unless validator.valid?(status) + fail ArgumentError, "invalid value for \"status\", must be one of #{validator.allowable_values}." + end + @status = status + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] token_source Object to be assigned + def token_source=(token_source) + validator = EnumAttributeValidator.new('String', ["api", "cli", "dashboard"]) + unless validator.valid?(token_source) + fail ArgumentError, "invalid value for \"token_source\", must be one of #{validator.allowable_values}." + end + @token_source = token_source + end + + # Custom attribute writer method with validation + # @param [Object] created_at Value to be assigned + def created_at=(created_at) + if created_at.nil? + fail ArgumentError, 'created_at cannot be nil' + end + + @created_at = created_at + end + + # Custom attribute writer method with validation + # @param [Object] all_time_requests Value to be assigned + def all_time_requests=(all_time_requests) + if all_time_requests.nil? + fail ArgumentError, 'all_time_requests cannot be nil' + end + + @all_time_requests = all_time_requests + end + + # Custom attribute writer method with validation + # @param [Object] token Value to be assigned + def token=(token) + if token.nil? + fail ArgumentError, 'token cannot be nil' + end + + @token = token + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + project_id == o.project_id && + name == o.name && + token_prefix == o.token_prefix && + scope == o.scope && + status == o.status && + token_source == o.token_source && + expires_at == o.expires_at && + last_used_at == o.last_used_at && + created_at == o.created_at && + all_time_requests == o.all_time_requests && + token == o.token + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, project_id, name, token_prefix, scope, status, token_source, expires_at, last_used_at, created_at, all_time_requests, token].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/frontend.rb b/lib/volcano/generated/lib/volcano-generated/models/frontend.rb index 69025e69..5e9b86c6 100644 --- a/lib/volcano/generated/lib/volcano-generated/models/frontend.rb +++ b/lib/volcano/generated/lib/volcano-generated/models/frontend.rb @@ -15,6 +15,12 @@ module Volcano::Generated class Frontend < ApiModelBase + # All preserves access to all project variables. Shared includes the project frontend shared-variable list. Scoped includes only explicitly declared variables in builds and runtime. Omission preserves the stored selection. + attr_accessor :variable_scope + + # Names selected when variable_scope is scoped. Missing declared values reject deployment. Omission preserves the stored list; an empty list clears it. + attr_accessor :declared_variables + attr_accessor :id attr_accessor :project_id @@ -79,6 +85,8 @@ def valid?(value) # Attribute mapping from ruby-style variable name to JSON key. def self.attribute_map { + :'variable_scope' => :'variable_scope', + :'declared_variables' => :'declared_variables', :'id' => :'id', :'project_id' => :'project_id', :'name' => :'name', @@ -111,6 +119,8 @@ def self.acceptable_attributes # Attribute type mapping. def self.openapi_types { + :'variable_scope' => :'String', + :'declared_variables' => :'Array', :'id' => :'String', :'project_id' => :'String', :'name' => :'String', @@ -152,6 +162,16 @@ def initialize(attributes = {}) h[k.to_sym] = v } + if attributes.key?(:'variable_scope') + self.variable_scope = attributes[:'variable_scope'] + end + + if attributes.key?(:'declared_variables') + if (value = attributes[:'declared_variables']).is_a?(Array) + self.declared_variables = value + end + end + if attributes.key?(:'id') self.id = attributes[:'id'] else @@ -292,6 +312,8 @@ def list_invalid_properties # @return true if the model is valid def valid? warn '[DEPRECATED] the `valid?` method is obsolete' + variable_scope_validator = EnumAttributeValidator.new('String', ["all", "shared", "scoped"]) + return false unless variable_scope_validator.valid?(@variable_scope) return false if @id.nil? return false if @project_id.nil? return false if @name.nil? @@ -312,6 +334,26 @@ def valid? true end + # Custom attribute writer method checking allowed values (enum). + # @param [Object] variable_scope Object to be assigned + def variable_scope=(variable_scope) + validator = EnumAttributeValidator.new('String', ["all", "shared", "scoped"]) + unless validator.valid?(variable_scope) + fail ArgumentError, "invalid value for \"variable_scope\", must be one of #{validator.allowable_values}." + end + @variable_scope = variable_scope + end + + # Custom attribute writer method with validation + # @param [Object] declared_variables Value to be assigned + def declared_variables=(declared_variables) + if declared_variables.nil? + fail ArgumentError, 'declared_variables cannot be nil' + end + + @declared_variables = declared_variables + end + # Custom attribute writer method with validation # @param [Object] id Value to be assigned def id=(id) @@ -430,6 +472,8 @@ def updated_at=(updated_at) def ==(o) return true if self.equal?(o) self.class == o.class && + variable_scope == o.variable_scope && + declared_variables == o.declared_variables && id == o.id && project_id == o.project_id && name == o.name && @@ -457,7 +501,7 @@ def eql?(o) # Calculates hash code according to all attributes. # @return [Integer] Hash code def hash - [id, project_id, name, framework, app_root, status, provisioning_started_at, deployed_regions, current_deployment_id, pending_deployment_id, site_url, custom_domain, custom_domain_status, last_invoked_at, created_at, updated_at].hash + [variable_scope, declared_variables, id, project_id, name, framework, app_root, status, provisioning_started_at, deployed_regions, current_deployment_id, pending_deployment_id, site_url, custom_domain, custom_domain_status, last_invoked_at, created_at, updated_at].hash end # Builds the object from hash diff --git a/lib/volcano/generated/lib/volcano-generated/models/frontend_function_route.rb b/lib/volcano/generated/lib/volcano-generated/models/frontend_function_route.rb new file mode 100644 index 00000000..7f6a676e --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/frontend_function_route.rb @@ -0,0 +1,375 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class FrontendFunctionRoute < ApiModelBase + attr_accessor :id + + attr_accessor :project_id + + attr_accessor :frontend_id + + attr_accessor :function_id + + attr_accessor :path_prefix + + attr_accessor :strip_prefix + + attr_accessor :created_at + + attr_accessor :updated_at + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'id' => :'id', + :'project_id' => :'project_id', + :'frontend_id' => :'frontend_id', + :'function_id' => :'function_id', + :'path_prefix' => :'path_prefix', + :'strip_prefix' => :'strip_prefix', + :'created_at' => :'created_at', + :'updated_at' => :'updated_at' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'id' => :'String', + :'project_id' => :'String', + :'frontend_id' => :'String', + :'function_id' => :'String', + :'path_prefix' => :'String', + :'strip_prefix' => :'Boolean', + :'created_at' => :'Time', + :'updated_at' => :'Time' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::FrontendFunctionRoute` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::FrontendFunctionRoute`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'project_id') + self.project_id = attributes[:'project_id'] + else + self.project_id = nil + end + + if attributes.key?(:'frontend_id') + self.frontend_id = attributes[:'frontend_id'] + else + self.frontend_id = nil + end + + if attributes.key?(:'function_id') + self.function_id = attributes[:'function_id'] + else + self.function_id = nil + end + + if attributes.key?(:'path_prefix') + self.path_prefix = attributes[:'path_prefix'] + else + self.path_prefix = nil + end + + if attributes.key?(:'strip_prefix') + self.strip_prefix = attributes[:'strip_prefix'] + else + self.strip_prefix = nil + end + + if attributes.key?(:'created_at') + self.created_at = attributes[:'created_at'] + else + self.created_at = nil + end + + if attributes.key?(:'updated_at') + self.updated_at = attributes[:'updated_at'] + else + self.updated_at = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @id.nil? + invalid_properties.push('invalid value for "id", id cannot be nil.') + end + + if @project_id.nil? + invalid_properties.push('invalid value for "project_id", project_id cannot be nil.') + end + + if @frontend_id.nil? + invalid_properties.push('invalid value for "frontend_id", frontend_id cannot be nil.') + end + + if @function_id.nil? + invalid_properties.push('invalid value for "function_id", function_id cannot be nil.') + end + + if @path_prefix.nil? + invalid_properties.push('invalid value for "path_prefix", path_prefix cannot be nil.') + end + + if @path_prefix.to_s.length > 512 + invalid_properties.push('invalid value for "path_prefix", the character length must be smaller than or equal to 512.') + end + + if @path_prefix.to_s.length < 2 + invalid_properties.push('invalid value for "path_prefix", the character length must be greater than or equal to 2.') + end + + pattern = Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + if @path_prefix !~ pattern + invalid_properties.push("invalid value for \"path_prefix\", must conform to the pattern #{pattern}.") + end + + if @strip_prefix.nil? + invalid_properties.push('invalid value for "strip_prefix", strip_prefix cannot be nil.') + end + + if @created_at.nil? + invalid_properties.push('invalid value for "created_at", created_at cannot be nil.') + end + + if @updated_at.nil? + invalid_properties.push('invalid value for "updated_at", updated_at cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @id.nil? + return false if @project_id.nil? + return false if @frontend_id.nil? + return false if @function_id.nil? + return false if @path_prefix.nil? + return false if @path_prefix.to_s.length > 512 + return false if @path_prefix.to_s.length < 2 + return false if @path_prefix !~ Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + return false if @strip_prefix.nil? + return false if @created_at.nil? + return false if @updated_at.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] id Value to be assigned + def id=(id) + if id.nil? + fail ArgumentError, 'id cannot be nil' + end + + @id = id + end + + # Custom attribute writer method with validation + # @param [Object] project_id Value to be assigned + def project_id=(project_id) + if project_id.nil? + fail ArgumentError, 'project_id cannot be nil' + end + + @project_id = project_id + end + + # Custom attribute writer method with validation + # @param [Object] frontend_id Value to be assigned + def frontend_id=(frontend_id) + if frontend_id.nil? + fail ArgumentError, 'frontend_id cannot be nil' + end + + @frontend_id = frontend_id + end + + # Custom attribute writer method with validation + # @param [Object] function_id Value to be assigned + def function_id=(function_id) + if function_id.nil? + fail ArgumentError, 'function_id cannot be nil' + end + + @function_id = function_id + end + + # Custom attribute writer method with validation + # @param [Object] path_prefix Value to be assigned + def path_prefix=(path_prefix) + if path_prefix.nil? + fail ArgumentError, 'path_prefix cannot be nil' + end + + if path_prefix.to_s.length > 512 + fail ArgumentError, 'invalid value for "path_prefix", the character length must be smaller than or equal to 512.' + end + + if path_prefix.to_s.length < 2 + fail ArgumentError, 'invalid value for "path_prefix", the character length must be greater than or equal to 2.' + end + + pattern = Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + if path_prefix !~ pattern + fail ArgumentError, "invalid value for \"path_prefix\", must conform to the pattern #{pattern}." + end + + @path_prefix = path_prefix + end + + # Custom attribute writer method with validation + # @param [Object] strip_prefix Value to be assigned + def strip_prefix=(strip_prefix) + if strip_prefix.nil? + fail ArgumentError, 'strip_prefix cannot be nil' + end + + @strip_prefix = strip_prefix + end + + # Custom attribute writer method with validation + # @param [Object] created_at Value to be assigned + def created_at=(created_at) + if created_at.nil? + fail ArgumentError, 'created_at cannot be nil' + end + + @created_at = created_at + end + + # Custom attribute writer method with validation + # @param [Object] updated_at Value to be assigned + def updated_at=(updated_at) + if updated_at.nil? + fail ArgumentError, 'updated_at cannot be nil' + end + + @updated_at = updated_at + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + project_id == o.project_id && + frontend_id == o.frontend_id && + function_id == o.function_id && + path_prefix == o.path_prefix && + strip_prefix == o.strip_prefix && + created_at == o.created_at && + updated_at == o.updated_at + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, project_id, frontend_id, function_id, path_prefix, strip_prefix, created_at, updated_at].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/frontend_function_route_list.rb b/lib/volcano/generated/lib/volcano-generated/models/frontend_function_route_list.rb new file mode 100644 index 00000000..626c6b11 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/frontend_function_route_list.rb @@ -0,0 +1,166 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class FrontendFunctionRouteList < ApiModelBase + attr_accessor :data + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'data' => :'data' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'data' => :'Array' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::FrontendFunctionRouteList` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::FrontendFunctionRouteList`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'data') + if (value = attributes[:'data']).is_a?(Array) + self.data = value + end + else + self.data = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @data.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + @data = data + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + data == o.data + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [data].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/function.rb b/lib/volcano/generated/lib/volcano-generated/models/function.rb index cec760c9..ca6a0043 100644 --- a/lib/volcano/generated/lib/volcano-generated/models/function.rb +++ b/lib/volcano/generated/lib/volcano-generated/models/function.rb @@ -41,7 +41,7 @@ class Function < ApiModelBase attr_accessor :aws_function_arn - # Canonical GeoDNS endpoint URL for invoking this function (always HTTPS) + # Canonical geo-routed HTTPS endpoint for invoking this function. Use it as-is: it does not share a domain with the API, so a host derived from the API URL will not reach the function. Omitted when the deployment serves no public invocation domain, as in local development, so a client testing for an empty string never matches. attr_accessor :invoke_url # Regions where this function is currently deployed diff --git a/lib/volcano/generated/lib/volcano-generated/models/paginated_project_access_tokens.rb b/lib/volcano/generated/lib/volcano-generated/models/paginated_project_access_tokens.rb new file mode 100644 index 00000000..47ded4e8 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/paginated_project_access_tokens.rb @@ -0,0 +1,279 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class PaginatedProjectAccessTokens < ApiModelBase + attr_accessor :data + + attr_accessor :page + + attr_accessor :limit + + attr_accessor :total + + attr_accessor :has_more + + attr_accessor :_next + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'data' => :'data', + :'page' => :'page', + :'limit' => :'limit', + :'total' => :'total', + :'has_more' => :'has_more', + :'_next' => :'next' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'data' => :'Array', + :'page' => :'Integer', + :'limit' => :'Integer', + :'total' => :'Integer', + :'has_more' => :'Boolean', + :'_next' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::PaginatedProjectAccessTokens` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::PaginatedProjectAccessTokens`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'data') + if (value = attributes[:'data']).is_a?(Array) + self.data = value + end + else + self.data = nil + end + + if attributes.key?(:'page') + self.page = attributes[:'page'] + else + self.page = nil + end + + if attributes.key?(:'limit') + self.limit = attributes[:'limit'] + else + self.limit = nil + end + + if attributes.key?(:'total') + self.total = attributes[:'total'] + else + self.total = nil + end + + if attributes.key?(:'has_more') + self.has_more = attributes[:'has_more'] + else + self.has_more = nil + end + + if attributes.key?(:'_next') + self._next = attributes[:'_next'] + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + if @page.nil? + invalid_properties.push('invalid value for "page", page cannot be nil.') + end + + if @limit.nil? + invalid_properties.push('invalid value for "limit", limit cannot be nil.') + end + + if @total.nil? + invalid_properties.push('invalid value for "total", total cannot be nil.') + end + + if @has_more.nil? + invalid_properties.push('invalid value for "has_more", has_more cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @data.nil? + return false if @page.nil? + return false if @limit.nil? + return false if @total.nil? + return false if @has_more.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + @data = data + end + + # Custom attribute writer method with validation + # @param [Object] page Value to be assigned + def page=(page) + if page.nil? + fail ArgumentError, 'page cannot be nil' + end + + @page = page + end + + # Custom attribute writer method with validation + # @param [Object] limit Value to be assigned + def limit=(limit) + if limit.nil? + fail ArgumentError, 'limit cannot be nil' + end + + @limit = limit + end + + # Custom attribute writer method with validation + # @param [Object] total Value to be assigned + def total=(total) + if total.nil? + fail ArgumentError, 'total cannot be nil' + end + + @total = total + end + + # Custom attribute writer method with validation + # @param [Object] has_more Value to be assigned + def has_more=(has_more) + if has_more.nil? + fail ArgumentError, 'has_more cannot be nil' + end + + @has_more = has_more + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + data == o.data && + page == o.page && + limit == o.limit && + total == o.total && + has_more == o.has_more && + _next == o._next + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [data, page, limit, total, has_more, _next].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/project_access_token.rb b/lib/volcano/generated/lib/volcano-generated/models/project_access_token.rb new file mode 100644 index 00000000..b665fe93 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/project_access_token.rb @@ -0,0 +1,426 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + # A project access token: a control-plane credential bound to a single project. Unlike a platform token, which acts on every project its owner has, this one is limited to the project it was created in. The secret itself is never returned here. Only its hash is stored, so the plaintext exists solely in the response to the create call. + class ProjectAccessToken < ApiModelBase + attr_accessor :id + + attr_accessor :project_id + + # Unique per project. + attr_accessor :name + + # First 12 characters of the secret, for recognising a token in a list. + attr_accessor :token_prefix + + attr_accessor :scope + + # `revoked` means the token was deliberately revoked, by you or by the deletion of its project. `expired` means it simply reached `expires_at`; nothing was taken away. Both are refused, and both keep their record so a token's name, prefix, last use, and request history remain available after a leak. A token revoked before its expiry passed stays `revoked`, because that is the fact worth keeping. + attr_accessor :status + + # What created the token. + attr_accessor :token_source + + # Absent for a token that does not expire. + attr_accessor :expires_at + + # Updated at most once every few minutes, so it may lag slightly. + attr_accessor :last_used_at + + attr_accessor :created_at + + # Requests authenticated with this token since it was created. + attr_accessor :all_time_requests + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'id' => :'id', + :'project_id' => :'project_id', + :'name' => :'name', + :'token_prefix' => :'token_prefix', + :'scope' => :'scope', + :'status' => :'status', + :'token_source' => :'token_source', + :'expires_at' => :'expires_at', + :'last_used_at' => :'last_used_at', + :'created_at' => :'created_at', + :'all_time_requests' => :'all_time_requests' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'id' => :'String', + :'project_id' => :'String', + :'name' => :'String', + :'token_prefix' => :'String', + :'scope' => :'ProjectAccessTokenScope', + :'status' => :'String', + :'token_source' => :'String', + :'expires_at' => :'Time', + :'last_used_at' => :'Time', + :'created_at' => :'Time', + :'all_time_requests' => :'Integer' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + :'expires_at', + :'last_used_at', + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::ProjectAccessToken` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::ProjectAccessToken`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'project_id') + self.project_id = attributes[:'project_id'] + else + self.project_id = nil + end + + if attributes.key?(:'name') + self.name = attributes[:'name'] + else + self.name = nil + end + + if attributes.key?(:'token_prefix') + self.token_prefix = attributes[:'token_prefix'] + else + self.token_prefix = nil + end + + if attributes.key?(:'scope') + self.scope = attributes[:'scope'] + else + self.scope = nil + end + + if attributes.key?(:'status') + self.status = attributes[:'status'] + else + self.status = nil + end + + if attributes.key?(:'token_source') + self.token_source = attributes[:'token_source'] + else + self.token_source = nil + end + + if attributes.key?(:'expires_at') + self.expires_at = attributes[:'expires_at'] + end + + if attributes.key?(:'last_used_at') + self.last_used_at = attributes[:'last_used_at'] + end + + if attributes.key?(:'created_at') + self.created_at = attributes[:'created_at'] + else + self.created_at = nil + end + + if attributes.key?(:'all_time_requests') + self.all_time_requests = attributes[:'all_time_requests'] + else + self.all_time_requests = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @id.nil? + invalid_properties.push('invalid value for "id", id cannot be nil.') + end + + if @project_id.nil? + invalid_properties.push('invalid value for "project_id", project_id cannot be nil.') + end + + if @name.nil? + invalid_properties.push('invalid value for "name", name cannot be nil.') + end + + if @token_prefix.nil? + invalid_properties.push('invalid value for "token_prefix", token_prefix cannot be nil.') + end + + if @scope.nil? + invalid_properties.push('invalid value for "scope", scope cannot be nil.') + end + + if @status.nil? + invalid_properties.push('invalid value for "status", status cannot be nil.') + end + + if @token_source.nil? + invalid_properties.push('invalid value for "token_source", token_source cannot be nil.') + end + + if @created_at.nil? + invalid_properties.push('invalid value for "created_at", created_at cannot be nil.') + end + + if @all_time_requests.nil? + invalid_properties.push('invalid value for "all_time_requests", all_time_requests cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @id.nil? + return false if @project_id.nil? + return false if @name.nil? + return false if @token_prefix.nil? + return false if @scope.nil? + return false if @status.nil? + status_validator = EnumAttributeValidator.new('String', ["active", "revoked", "expired"]) + return false unless status_validator.valid?(@status) + return false if @token_source.nil? + token_source_validator = EnumAttributeValidator.new('String', ["api", "cli", "dashboard"]) + return false unless token_source_validator.valid?(@token_source) + return false if @created_at.nil? + return false if @all_time_requests.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] id Value to be assigned + def id=(id) + if id.nil? + fail ArgumentError, 'id cannot be nil' + end + + @id = id + end + + # Custom attribute writer method with validation + # @param [Object] project_id Value to be assigned + def project_id=(project_id) + if project_id.nil? + fail ArgumentError, 'project_id cannot be nil' + end + + @project_id = project_id + end + + # Custom attribute writer method with validation + # @param [Object] name Value to be assigned + def name=(name) + if name.nil? + fail ArgumentError, 'name cannot be nil' + end + + @name = name + end + + # Custom attribute writer method with validation + # @param [Object] token_prefix Value to be assigned + def token_prefix=(token_prefix) + if token_prefix.nil? + fail ArgumentError, 'token_prefix cannot be nil' + end + + @token_prefix = token_prefix + end + + # Custom attribute writer method with validation + # @param [Object] scope Value to be assigned + def scope=(scope) + if scope.nil? + fail ArgumentError, 'scope cannot be nil' + end + + @scope = scope + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] status Object to be assigned + def status=(status) + validator = EnumAttributeValidator.new('String', ["active", "revoked", "expired"]) + unless validator.valid?(status) + fail ArgumentError, "invalid value for \"status\", must be one of #{validator.allowable_values}." + end + @status = status + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] token_source Object to be assigned + def token_source=(token_source) + validator = EnumAttributeValidator.new('String', ["api", "cli", "dashboard"]) + unless validator.valid?(token_source) + fail ArgumentError, "invalid value for \"token_source\", must be one of #{validator.allowable_values}." + end + @token_source = token_source + end + + # Custom attribute writer method with validation + # @param [Object] created_at Value to be assigned + def created_at=(created_at) + if created_at.nil? + fail ArgumentError, 'created_at cannot be nil' + end + + @created_at = created_at + end + + # Custom attribute writer method with validation + # @param [Object] all_time_requests Value to be assigned + def all_time_requests=(all_time_requests) + if all_time_requests.nil? + fail ArgumentError, 'all_time_requests cannot be nil' + end + + @all_time_requests = all_time_requests + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + project_id == o.project_id && + name == o.name && + token_prefix == o.token_prefix && + scope == o.scope && + status == o.status && + token_source == o.token_source && + expires_at == o.expires_at && + last_used_at == o.last_used_at && + created_at == o.created_at && + all_time_requests == o.all_time_requests + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, project_id, name, token_prefix, scope, status, token_source, expires_at, last_used_at, created_at, all_time_requests].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/project_access_token_scope.rb b/lib/volcano/generated/lib/volcano-generated/models/project_access_token_scope.rb new file mode 100644 index 00000000..e0fb177f --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/project_access_token_scope.rb @@ -0,0 +1,40 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class ProjectAccessTokenScope + ProjectAccessTokenScopeFull = "full".freeze + ProjectAccessTokenScopeReadOnly = "read_only".freeze + + def self.all_vars + @all_vars ||= [ProjectAccessTokenScopeFull, ProjectAccessTokenScopeReadOnly].freeze + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def self.build_from_hash(value) + new.build_from_hash(value) + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def build_from_hash(value) + return value if ProjectAccessTokenScope.all_vars.include?(value) + raise "Invalid ENUM value #{value} for class #ProjectAccessTokenScope" + end + end +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/project_access_token_usage.rb b/lib/volcano/generated/lib/volcano-generated/models/project_access_token_usage.rb new file mode 100644 index 00000000..4e06e73b --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/project_access_token_usage.rb @@ -0,0 +1,299 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + # Zero-filled daily request counts for a single token, oldest first. Every day in the window is present, so a gap reads as zero rather than missing. Counts every request the token authenticated, including ones then refused — a read-only token attempting a write, or a token presented on another project's route. That is deliberate: after a leak, the probing is the part you want to see, and a counter that hid it would make a token look idle while it was being tried. + class ProjectAccessTokenUsage < ApiModelBase + attr_accessor :token_id + + attr_accessor :name + + # The token's display prefix, which identifies the credential when its name does not. Revoking frees a name, so a project that rotated `ci-deploy` has two entries here both called `ci-deploy`. Not usable as a credential. + attr_accessor :token_prefix + + # Number of daily entries returned, always equal to the requested window. + attr_accessor :days + + attr_accessor :daily + + attr_accessor :total_requests + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'token_id' => :'token_id', + :'name' => :'name', + :'token_prefix' => :'token_prefix', + :'days' => :'days', + :'daily' => :'daily', + :'total_requests' => :'total_requests' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'token_id' => :'String', + :'name' => :'String', + :'token_prefix' => :'String', + :'days' => :'Integer', + :'daily' => :'Array', + :'total_requests' => :'Integer' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::ProjectAccessTokenUsage` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::ProjectAccessTokenUsage`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'token_id') + self.token_id = attributes[:'token_id'] + else + self.token_id = nil + end + + if attributes.key?(:'name') + self.name = attributes[:'name'] + else + self.name = nil + end + + if attributes.key?(:'token_prefix') + self.token_prefix = attributes[:'token_prefix'] + else + self.token_prefix = nil + end + + if attributes.key?(:'days') + self.days = attributes[:'days'] + else + self.days = nil + end + + if attributes.key?(:'daily') + if (value = attributes[:'daily']).is_a?(Array) + self.daily = value + end + else + self.daily = nil + end + + if attributes.key?(:'total_requests') + self.total_requests = attributes[:'total_requests'] + else + self.total_requests = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @token_id.nil? + invalid_properties.push('invalid value for "token_id", token_id cannot be nil.') + end + + if @name.nil? + invalid_properties.push('invalid value for "name", name cannot be nil.') + end + + if @token_prefix.nil? + invalid_properties.push('invalid value for "token_prefix", token_prefix cannot be nil.') + end + + if @days.nil? + invalid_properties.push('invalid value for "days", days cannot be nil.') + end + + if @daily.nil? + invalid_properties.push('invalid value for "daily", daily cannot be nil.') + end + + if @total_requests.nil? + invalid_properties.push('invalid value for "total_requests", total_requests cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @token_id.nil? + return false if @name.nil? + return false if @token_prefix.nil? + return false if @days.nil? + return false if @daily.nil? + return false if @total_requests.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] token_id Value to be assigned + def token_id=(token_id) + if token_id.nil? + fail ArgumentError, 'token_id cannot be nil' + end + + @token_id = token_id + end + + # Custom attribute writer method with validation + # @param [Object] name Value to be assigned + def name=(name) + if name.nil? + fail ArgumentError, 'name cannot be nil' + end + + @name = name + end + + # Custom attribute writer method with validation + # @param [Object] token_prefix Value to be assigned + def token_prefix=(token_prefix) + if token_prefix.nil? + fail ArgumentError, 'token_prefix cannot be nil' + end + + @token_prefix = token_prefix + end + + # Custom attribute writer method with validation + # @param [Object] days Value to be assigned + def days=(days) + if days.nil? + fail ArgumentError, 'days cannot be nil' + end + + @days = days + end + + # Custom attribute writer method with validation + # @param [Object] daily Value to be assigned + def daily=(daily) + if daily.nil? + fail ArgumentError, 'daily cannot be nil' + end + + @daily = daily + end + + # Custom attribute writer method with validation + # @param [Object] total_requests Value to be assigned + def total_requests=(total_requests) + if total_requests.nil? + fail ArgumentError, 'total_requests cannot be nil' + end + + @total_requests = total_requests + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + token_id == o.token_id && + name == o.name && + token_prefix == o.token_prefix && + days == o.days && + daily == o.daily && + total_requests == o.total_requests + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [token_id, name, token_prefix, days, daily, total_requests].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/project_access_token_usage_daily_entry.rb b/lib/volcano/generated/lib/volcano-generated/models/project_access_token_usage_daily_entry.rb new file mode 100644 index 00000000..a5b9d82c --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/project_access_token_usage_daily_entry.rb @@ -0,0 +1,191 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class ProjectAccessTokenUsageDailyEntry < ApiModelBase + # UTC day. + attr_accessor :day + + attr_accessor :requests + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'day' => :'day', + :'requests' => :'requests' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'day' => :'Date', + :'requests' => :'Integer' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::ProjectAccessTokenUsageDailyEntry` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::ProjectAccessTokenUsageDailyEntry`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'day') + self.day = attributes[:'day'] + else + self.day = nil + end + + if attributes.key?(:'requests') + self.requests = attributes[:'requests'] + else + self.requests = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @day.nil? + invalid_properties.push('invalid value for "day", day cannot be nil.') + end + + if @requests.nil? + invalid_properties.push('invalid value for "requests", requests cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @day.nil? + return false if @requests.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] day Value to be assigned + def day=(day) + if day.nil? + fail ArgumentError, 'day cannot be nil' + end + + @day = day + end + + # Custom attribute writer method with validation + # @param [Object] requests Value to be assigned + def requests=(requests) + if requests.nil? + fail ArgumentError, 'requests cannot be nil' + end + + @requests = requests + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + day == o.day && + requests == o.requests + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [day, requests].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/project_config.rb b/lib/volcano/generated/lib/volcano-generated/models/project_config.rb index c95bfced..cc9f2e1e 100644 --- a/lib/volcano/generated/lib/volcano-generated/models/project_config.rb +++ b/lib/volcano/generated/lib/volcano-generated/models/project_config.rb @@ -26,6 +26,9 @@ class ProjectConfig < ApiModelBase # Replace the complete shared function-variable list with existing names, without changing variable values. Omission keeps membership unchanged; an empty list clears it. attr_accessor :shared_variables + # Replace the complete shared frontend-variable list with existing names. Frontends with variable_scope shared receive this list. Omission keeps membership unchanged; an empty list clears it. + attr_accessor :frontend_shared_variables + # Fully synced when declared - variables absent from this list are deleted. attr_accessor :variables @@ -68,6 +71,7 @@ def self.attribute_map :'project' => :'project', :'databases' => :'databases', :'shared_variables' => :'shared_variables', + :'frontend_shared_variables' => :'frontend_shared_variables', :'variables' => :'variables', :'buckets' => :'buckets', :'realtime' => :'realtime', @@ -94,6 +98,7 @@ def self.openapi_types :'project' => :'ProjectConfigProject', :'databases' => :'Array', :'shared_variables' => :'Array', + :'frontend_shared_variables' => :'Array', :'variables' => :'Array', :'buckets' => :'Array', :'realtime' => :'ProjectConfigRealtime', @@ -147,6 +152,12 @@ def initialize(attributes = {}) end end + if attributes.key?(:'frontend_shared_variables') + if (value = attributes[:'frontend_shared_variables']).is_a?(Array) + self.frontend_shared_variables = value + end + end + if attributes.key?(:'variables') if (value = attributes[:'variables']).is_a?(Array) self.variables = value @@ -222,6 +233,16 @@ def shared_variables=(shared_variables) @shared_variables = shared_variables end + # Custom attribute writer method with validation + # @param [Object] frontend_shared_variables Value to be assigned + def frontend_shared_variables=(frontend_shared_variables) + if frontend_shared_variables.nil? + fail ArgumentError, 'frontend_shared_variables cannot be nil' + end + + @frontend_shared_variables = frontend_shared_variables + end + # Checks equality by comparing each attribute. # @param [Object] Object to be compared def ==(o) @@ -231,6 +252,7 @@ def ==(o) project == o.project && databases == o.databases && shared_variables == o.shared_variables && + frontend_shared_variables == o.frontend_shared_variables && variables == o.variables && buckets == o.buckets && realtime == o.realtime && @@ -248,7 +270,7 @@ def eql?(o) # Calculates hash code according to all attributes. # @return [Integer] Hash code def hash - [version, project, databases, shared_variables, variables, buckets, realtime, auth, functions, frontends].hash + [version, project, databases, shared_variables, frontend_shared_variables, variables, buckets, realtime, auth, functions, frontends].hash end # Builds the object from hash diff --git a/lib/volcano/generated/lib/volcano-generated/models/project_config_frontend.rb b/lib/volcano/generated/lib/volcano-generated/models/project_config_frontend.rb index 740c301f..072ed4b6 100644 --- a/lib/volcano/generated/lib/volcano-generated/models/project_config_frontend.rb +++ b/lib/volcano/generated/lib/volcano-generated/models/project_config_frontend.rb @@ -16,15 +16,49 @@ module Volcano::Generated # Configuration for an existing (deployed) frontend. Frontends are never created or deleted through the manifest. A declared frontend entry without `custom_domain` deletes an existing custom domain. class ProjectConfigFrontend < ApiModelBase + # All preserves access to all project variables. Shared includes the project frontend_shared_variables list. Scoped includes only explicitly declared variables in builds and runtime. Omission preserves the stored selection. + attr_accessor :variable_scope + + # Names selected when variable_scope is scoped. Missing declared values reject deployment. Omission preserves the stored list; an empty list clears it. + attr_accessor :variables + attr_accessor :name attr_accessor :custom_domain + # Complete set of same-origin Function path mappings when declared. Omission preserves existing mappings; an empty list deletes all mappings. + attr_accessor :function_routes + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + # Attribute mapping from ruby-style variable name to JSON key. def self.attribute_map { + :'variable_scope' => :'variable_scope', + :'variables' => :'variables', :'name' => :'name', - :'custom_domain' => :'custom_domain' + :'custom_domain' => :'custom_domain', + :'function_routes' => :'function_routes' } end @@ -41,8 +75,11 @@ def self.acceptable_attributes # Attribute type mapping. def self.openapi_types { + :'variable_scope' => :'String', + :'variables' => :'Array', :'name' => :'String', - :'custom_domain' => :'ProjectConfigCustomDomain' + :'custom_domain' => :'ProjectConfigCustomDomain', + :'function_routes' => :'Array' } end @@ -68,6 +105,16 @@ def initialize(attributes = {}) h[k.to_sym] = v } + if attributes.key?(:'variable_scope') + self.variable_scope = attributes[:'variable_scope'] + end + + if attributes.key?(:'variables') + if (value = attributes[:'variables']).is_a?(Array) + self.variables = value + end + end + if attributes.key?(:'name') self.name = attributes[:'name'] else @@ -77,6 +124,12 @@ def initialize(attributes = {}) if attributes.key?(:'custom_domain') self.custom_domain = attributes[:'custom_domain'] end + + if attributes.key?(:'function_routes') + if (value = attributes[:'function_routes']).is_a?(Array) + self.function_routes = value + end + end end # Show invalid properties with the reasons. Usually used together with valid? @@ -92,6 +145,10 @@ def list_invalid_properties invalid_properties.push('invalid value for "name", the character length must be greater than or equal to 1.') end + if !@function_routes.nil? && @function_routes.length > 64 + invalid_properties.push('invalid value for "function_routes", number of items must be less than or equal to 64.') + end + invalid_properties end @@ -99,11 +156,34 @@ def list_invalid_properties # @return true if the model is valid def valid? warn '[DEPRECATED] the `valid?` method is obsolete' + variable_scope_validator = EnumAttributeValidator.new('String', ["all", "shared", "scoped"]) + return false unless variable_scope_validator.valid?(@variable_scope) return false if @name.nil? return false if @name.to_s.length < 1 + return false if !@function_routes.nil? && @function_routes.length > 64 true end + # Custom attribute writer method checking allowed values (enum). + # @param [Object] variable_scope Object to be assigned + def variable_scope=(variable_scope) + validator = EnumAttributeValidator.new('String', ["all", "shared", "scoped"]) + unless validator.valid?(variable_scope) + fail ArgumentError, "invalid value for \"variable_scope\", must be one of #{validator.allowable_values}." + end + @variable_scope = variable_scope + end + + # Custom attribute writer method with validation + # @param [Object] variables Value to be assigned + def variables=(variables) + if variables.nil? + fail ArgumentError, 'variables cannot be nil' + end + + @variables = variables + end + # Custom attribute writer method with validation # @param [Object] name Value to be assigned def name=(name) @@ -118,13 +198,30 @@ def name=(name) @name = name end + # Custom attribute writer method with validation + # @param [Object] function_routes Value to be assigned + def function_routes=(function_routes) + if function_routes.nil? + fail ArgumentError, 'function_routes cannot be nil' + end + + if function_routes.length > 64 + fail ArgumentError, 'invalid value for "function_routes", number of items must be less than or equal to 64.' + end + + @function_routes = function_routes + end + # Checks equality by comparing each attribute. # @param [Object] Object to be compared def ==(o) return true if self.equal?(o) self.class == o.class && + variable_scope == o.variable_scope && + variables == o.variables && name == o.name && - custom_domain == o.custom_domain + custom_domain == o.custom_domain && + function_routes == o.function_routes end # @see the `==` method @@ -136,7 +233,7 @@ def eql?(o) # Calculates hash code according to all attributes. # @return [Integer] Hash code def hash - [name, custom_domain].hash + [variable_scope, variables, name, custom_domain, function_routes].hash end # Builds the object from hash diff --git a/lib/volcano/generated/lib/volcano-generated/models/project_config_frontend_function_route.rb b/lib/volcano/generated/lib/volcano-generated/models/project_config_frontend_function_route.rb new file mode 100644 index 00000000..936c619e --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/project_config_frontend_function_route.rb @@ -0,0 +1,240 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class ProjectConfigFrontendFunctionRoute < ApiModelBase + # Name of an existing standard Function configured for HTTP invocation. + attr_accessor :function + + attr_accessor :path_prefix + + attr_accessor :strip_prefix + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'function' => :'function', + :'path_prefix' => :'path_prefix', + :'strip_prefix' => :'strip_prefix' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'function' => :'String', + :'path_prefix' => :'String', + :'strip_prefix' => :'Boolean' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::ProjectConfigFrontendFunctionRoute` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::ProjectConfigFrontendFunctionRoute`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'function') + self.function = attributes[:'function'] + else + self.function = nil + end + + if attributes.key?(:'path_prefix') + self.path_prefix = attributes[:'path_prefix'] + else + self.path_prefix = nil + end + + if attributes.key?(:'strip_prefix') + self.strip_prefix = attributes[:'strip_prefix'] + else + self.strip_prefix = false + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @function.nil? + invalid_properties.push('invalid value for "function", function cannot be nil.') + end + + if @function.to_s.length < 1 + invalid_properties.push('invalid value for "function", the character length must be greater than or equal to 1.') + end + + if @path_prefix.nil? + invalid_properties.push('invalid value for "path_prefix", path_prefix cannot be nil.') + end + + if @path_prefix.to_s.length > 512 + invalid_properties.push('invalid value for "path_prefix", the character length must be smaller than or equal to 512.') + end + + if @path_prefix.to_s.length < 2 + invalid_properties.push('invalid value for "path_prefix", the character length must be greater than or equal to 2.') + end + + pattern = Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + if @path_prefix !~ pattern + invalid_properties.push("invalid value for \"path_prefix\", must conform to the pattern #{pattern}.") + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @function.nil? + return false if @function.to_s.length < 1 + return false if @path_prefix.nil? + return false if @path_prefix.to_s.length > 512 + return false if @path_prefix.to_s.length < 2 + return false if @path_prefix !~ Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + true + end + + # Custom attribute writer method with validation + # @param [Object] function Value to be assigned + def function=(function) + if function.nil? + fail ArgumentError, 'function cannot be nil' + end + + if function.to_s.length < 1 + fail ArgumentError, 'invalid value for "function", the character length must be greater than or equal to 1.' + end + + @function = function + end + + # Custom attribute writer method with validation + # @param [Object] path_prefix Value to be assigned + def path_prefix=(path_prefix) + if path_prefix.nil? + fail ArgumentError, 'path_prefix cannot be nil' + end + + if path_prefix.to_s.length > 512 + fail ArgumentError, 'invalid value for "path_prefix", the character length must be smaller than or equal to 512.' + end + + if path_prefix.to_s.length < 2 + fail ArgumentError, 'invalid value for "path_prefix", the character length must be greater than or equal to 2.' + end + + pattern = Regexp.new(/^\/[^?#\\]*[^\/?#\\]$/) + if path_prefix !~ pattern + fail ArgumentError, "invalid value for \"path_prefix\", must conform to the pattern #{pattern}." + end + + @path_prefix = path_prefix + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + function == o.function && + path_prefix == o.path_prefix && + strip_prefix == o.strip_prefix + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [function, path_prefix, strip_prefix].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/publish_sandbox_preset_request.rb b/lib/volcano/generated/lib/volcano-generated/models/publish_sandbox_preset_request.rb new file mode 100644 index 00000000..83ad4077 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/publish_sandbox_preset_request.rb @@ -0,0 +1,268 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class PublishSandboxPresetRequest < ApiModelBase + attr_accessor :id + + attr_accessor :preset + + attr_accessor :memory_mb + + attr_accessor :deployment_id + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'id' => :'id', + :'preset' => :'preset', + :'memory_mb' => :'memory_mb', + :'deployment_id' => :'deployment_id' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'id' => :'String', + :'preset' => :'String', + :'memory_mb' => :'Integer', + :'deployment_id' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::PublishSandboxPresetRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::PublishSandboxPresetRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'preset') + self.preset = attributes[:'preset'] + else + self.preset = nil + end + + if attributes.key?(:'memory_mb') + self.memory_mb = attributes[:'memory_mb'] + else + self.memory_mb = nil + end + + if attributes.key?(:'deployment_id') + self.deployment_id = attributes[:'deployment_id'] + else + self.deployment_id = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @id.nil? + invalid_properties.push('invalid value for "id", id cannot be nil.') + end + + if @preset.nil? + invalid_properties.push('invalid value for "preset", preset cannot be nil.') + end + + if @memory_mb.nil? + invalid_properties.push('invalid value for "memory_mb", memory_mb cannot be nil.') + end + + if @deployment_id.nil? + invalid_properties.push('invalid value for "deployment_id", deployment_id cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @id.nil? + return false if @preset.nil? + preset_validator = EnumAttributeValidator.new('String', ["python3.12", "node22"]) + return false unless preset_validator.valid?(@preset) + return false if @memory_mb.nil? + memory_mb_validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + return false unless memory_mb_validator.valid?(@memory_mb) + return false if @deployment_id.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] id Value to be assigned + def id=(id) + if id.nil? + fail ArgumentError, 'id cannot be nil' + end + + @id = id + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] preset Object to be assigned + def preset=(preset) + validator = EnumAttributeValidator.new('String', ["python3.12", "node22"]) + unless validator.valid?(preset) + fail ArgumentError, "invalid value for \"preset\", must be one of #{validator.allowable_values}." + end + @preset = preset + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] memory_mb Object to be assigned + def memory_mb=(memory_mb) + validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + unless validator.valid?(memory_mb) + fail ArgumentError, "invalid value for \"memory_mb\", must be one of #{validator.allowable_values}." + end + @memory_mb = memory_mb + end + + # Custom attribute writer method with validation + # @param [Object] deployment_id Value to be assigned + def deployment_id=(deployment_id) + if deployment_id.nil? + fail ArgumentError, 'deployment_id cannot be nil' + end + + @deployment_id = deployment_id + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + preset == o.preset && + memory_mb == o.memory_mb && + deployment_id == o.deployment_id + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, preset, memory_mb, deployment_id].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/replace_frontend_shared_variables_request.rb b/lib/volcano/generated/lib/volcano-generated/models/replace_frontend_shared_variables_request.rb new file mode 100644 index 00000000..1ebe0ff2 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/replace_frontend_shared_variables_request.rb @@ -0,0 +1,237 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class ReplaceFrontendSharedVariablesRequest < ApiModelBase + attr_accessor :frontend_shared_variables + + # When present, replace only if the current complete frontend shared list matches this list. + attr_accessor :expected_frontend_shared_variables + + # SHA-256 of the sorted unique current shared names joined by a newline. Use instead of expected_frontend_shared_variables for a compact conditional replacement. + attr_accessor :expected_frontend_shared_variables_digest + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'frontend_shared_variables' => :'frontend_shared_variables', + :'expected_frontend_shared_variables' => :'expected_frontend_shared_variables', + :'expected_frontend_shared_variables_digest' => :'expected_frontend_shared_variables_digest' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'frontend_shared_variables' => :'Array', + :'expected_frontend_shared_variables' => :'Array', + :'expected_frontend_shared_variables_digest' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::ReplaceFrontendSharedVariablesRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::ReplaceFrontendSharedVariablesRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'frontend_shared_variables') + if (value = attributes[:'frontend_shared_variables']).is_a?(Array) + self.frontend_shared_variables = value + end + else + self.frontend_shared_variables = nil + end + + if attributes.key?(:'expected_frontend_shared_variables') + if (value = attributes[:'expected_frontend_shared_variables']).is_a?(Array) + self.expected_frontend_shared_variables = value + end + end + + if attributes.key?(:'expected_frontend_shared_variables_digest') + self.expected_frontend_shared_variables_digest = attributes[:'expected_frontend_shared_variables_digest'] + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @frontend_shared_variables.nil? + invalid_properties.push('invalid value for "frontend_shared_variables", frontend_shared_variables cannot be nil.') + end + + if !@expected_frontend_shared_variables_digest.nil? && @expected_frontend_shared_variables_digest.to_s.length > 64 + invalid_properties.push('invalid value for "expected_frontend_shared_variables_digest", the character length must be smaller than or equal to 64.') + end + + if !@expected_frontend_shared_variables_digest.nil? && @expected_frontend_shared_variables_digest.to_s.length < 64 + invalid_properties.push('invalid value for "expected_frontend_shared_variables_digest", the character length must be greater than or equal to 64.') + end + + pattern = Regexp.new(/^[a-f0-9]{64}$/) + if !@expected_frontend_shared_variables_digest.nil? && @expected_frontend_shared_variables_digest !~ pattern + invalid_properties.push("invalid value for \"expected_frontend_shared_variables_digest\", must conform to the pattern #{pattern}.") + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @frontend_shared_variables.nil? + return false if !@expected_frontend_shared_variables_digest.nil? && @expected_frontend_shared_variables_digest.to_s.length > 64 + return false if !@expected_frontend_shared_variables_digest.nil? && @expected_frontend_shared_variables_digest.to_s.length < 64 + return false if !@expected_frontend_shared_variables_digest.nil? && @expected_frontend_shared_variables_digest !~ Regexp.new(/^[a-f0-9]{64}$/) + true + end + + # Custom attribute writer method with validation + # @param [Object] frontend_shared_variables Value to be assigned + def frontend_shared_variables=(frontend_shared_variables) + if frontend_shared_variables.nil? + fail ArgumentError, 'frontend_shared_variables cannot be nil' + end + + @frontend_shared_variables = frontend_shared_variables + end + + # Custom attribute writer method with validation + # @param [Object] expected_frontend_shared_variables Value to be assigned + def expected_frontend_shared_variables=(expected_frontend_shared_variables) + if expected_frontend_shared_variables.nil? + fail ArgumentError, 'expected_frontend_shared_variables cannot be nil' + end + + @expected_frontend_shared_variables = expected_frontend_shared_variables + end + + # Custom attribute writer method with validation + # @param [Object] expected_frontend_shared_variables_digest Value to be assigned + def expected_frontend_shared_variables_digest=(expected_frontend_shared_variables_digest) + if expected_frontend_shared_variables_digest.nil? + fail ArgumentError, 'expected_frontend_shared_variables_digest cannot be nil' + end + + if expected_frontend_shared_variables_digest.to_s.length > 64 + fail ArgumentError, 'invalid value for "expected_frontend_shared_variables_digest", the character length must be smaller than or equal to 64.' + end + + if expected_frontend_shared_variables_digest.to_s.length < 64 + fail ArgumentError, 'invalid value for "expected_frontend_shared_variables_digest", the character length must be greater than or equal to 64.' + end + + pattern = Regexp.new(/^[a-f0-9]{64}$/) + if expected_frontend_shared_variables_digest !~ pattern + fail ArgumentError, "invalid value for \"expected_frontend_shared_variables_digest\", must conform to the pattern #{pattern}." + end + + @expected_frontend_shared_variables_digest = expected_frontend_shared_variables_digest + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + frontend_shared_variables == o.frontend_shared_variables && + expected_frontend_shared_variables == o.expected_frontend_shared_variables && + expected_frontend_shared_variables_digest == o.expected_frontend_shared_variables_digest + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [frontend_shared_variables, expected_frontend_shared_variables, expected_frontend_shared_variables_digest].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/resolve_function_response.rb b/lib/volcano/generated/lib/volcano-generated/models/resolve_function_response.rb index d231ecfa..d7107e95 100644 --- a/lib/volcano/generated/lib/volcano-generated/models/resolve_function_response.rb +++ b/lib/volcano/generated/lib/volcano-generated/models/resolve_function_response.rb @@ -21,6 +21,9 @@ class ResolveFunctionResponse < ApiModelBase # Canonical function ID used for invocation routing attr_accessor :function_id + # Canonical HTTPS endpoint for invoking this function. Use it as-is: it does not share a domain with the API, so a host derived from the API URL will not reach the function. Omitted when the deployment serves no public invocation domain, as in local development; invoke through POST /functions/{functionId}/invoke instead. + attr_accessor :invoke_url + # Suggested SDK cache TTL for this name-to-ID mapping attr_accessor :cache_ttl_seconds @@ -29,6 +32,7 @@ def self.attribute_map { :'name' => :'name', :'function_id' => :'function_id', + :'invoke_url' => :'invoke_url', :'cache_ttl_seconds' => :'cache_ttl_seconds' } end @@ -48,6 +52,7 @@ def self.openapi_types { :'name' => :'String', :'function_id' => :'String', + :'invoke_url' => :'String', :'cache_ttl_seconds' => :'Integer' } end @@ -86,6 +91,10 @@ def initialize(attributes = {}) self.function_id = nil end + if attributes.key?(:'invoke_url') + self.invoke_url = attributes[:'invoke_url'] + end + if attributes.key?(:'cache_ttl_seconds') self.cache_ttl_seconds = attributes[:'cache_ttl_seconds'] else @@ -189,6 +198,7 @@ def ==(o) self.class == o.class && name == o.name && function_id == o.function_id && + invoke_url == o.invoke_url && cache_ttl_seconds == o.cache_ttl_seconds end @@ -201,7 +211,7 @@ def eql?(o) # Calculates hash code according to all attributes. # @return [Integer] Hash code def hash - [name, function_id, cache_ttl_seconds].hash + [name, function_id, invoke_url, cache_ttl_seconds].hash end # Builds the object from hash diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_access.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_access.rb new file mode 100644 index 00000000..4ae272fa --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_access.rb @@ -0,0 +1,216 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxAccess < ApiModelBase + attr_accessor :url + + attr_accessor :token + + attr_accessor :expires_at + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'url' => :'url', + :'token' => :'token', + :'expires_at' => :'expires_at' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'url' => :'String', + :'token' => :'String', + :'expires_at' => :'Time' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxAccess` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxAccess`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'url') + self.url = attributes[:'url'] + else + self.url = nil + end + + if attributes.key?(:'token') + self.token = attributes[:'token'] + else + self.token = nil + end + + if attributes.key?(:'expires_at') + self.expires_at = attributes[:'expires_at'] + else + self.expires_at = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @url.nil? + invalid_properties.push('invalid value for "url", url cannot be nil.') + end + + if @token.nil? + invalid_properties.push('invalid value for "token", token cannot be nil.') + end + + if @expires_at.nil? + invalid_properties.push('invalid value for "expires_at", expires_at cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @url.nil? + return false if @token.nil? + return false if @expires_at.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] url Value to be assigned + def url=(url) + if url.nil? + fail ArgumentError, 'url cannot be nil' + end + + @url = url + end + + # Custom attribute writer method with validation + # @param [Object] token Value to be assigned + def token=(token) + if token.nil? + fail ArgumentError, 'token cannot be nil' + end + + @token = token + end + + # Custom attribute writer method with validation + # @param [Object] expires_at Value to be assigned + def expires_at=(expires_at) + if expires_at.nil? + fail ArgumentError, 'expires_at cannot be nil' + end + + @expires_at = expires_at + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + url == o.url && + token == o.token && + expires_at == o.expires_at + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [url, token, expires_at].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_access_request.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_access_request.rb new file mode 100644 index 00000000..530c1754 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_access_request.rb @@ -0,0 +1,221 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxAccessRequest < ApiModelBase + attr_accessor :port + + attr_accessor :expires_in_seconds + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'port' => :'port', + :'expires_in_seconds' => :'expires_in_seconds' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'port' => :'Integer', + :'expires_in_seconds' => :'Integer' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxAccessRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxAccessRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'port') + self.port = attributes[:'port'] + else + self.port = nil + end + + if attributes.key?(:'expires_in_seconds') + self.expires_in_seconds = attributes[:'expires_in_seconds'] + else + self.expires_in_seconds = 300 + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @port.nil? + invalid_properties.push('invalid value for "port", port cannot be nil.') + end + + if @port > 65532 + invalid_properties.push('invalid value for "port", must be smaller than or equal to 65532.') + end + + if @port < 1 + invalid_properties.push('invalid value for "port", must be greater than or equal to 1.') + end + + if !@expires_in_seconds.nil? && @expires_in_seconds > 300 + invalid_properties.push('invalid value for "expires_in_seconds", must be smaller than or equal to 300.') + end + + if !@expires_in_seconds.nil? && @expires_in_seconds < 1 + invalid_properties.push('invalid value for "expires_in_seconds", must be greater than or equal to 1.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @port.nil? + return false if @port > 65532 + return false if @port < 1 + return false if !@expires_in_seconds.nil? && @expires_in_seconds > 300 + return false if !@expires_in_seconds.nil? && @expires_in_seconds < 1 + true + end + + # Custom attribute writer method with validation + # @param [Object] port Value to be assigned + def port=(port) + if port.nil? + fail ArgumentError, 'port cannot be nil' + end + + if port > 65532 + fail ArgumentError, 'invalid value for "port", must be smaller than or equal to 65532.' + end + + if port < 1 + fail ArgumentError, 'invalid value for "port", must be greater than or equal to 1.' + end + + @port = port + end + + # Custom attribute writer method with validation + # @param [Object] expires_in_seconds Value to be assigned + def expires_in_seconds=(expires_in_seconds) + if expires_in_seconds.nil? + fail ArgumentError, 'expires_in_seconds cannot be nil' + end + + if expires_in_seconds > 300 + fail ArgumentError, 'invalid value for "expires_in_seconds", must be smaller than or equal to 300.' + end + + if expires_in_seconds < 1 + fail ArgumentError, 'invalid value for "expires_in_seconds", must be greater than or equal to 1.' + end + + @expires_in_seconds = expires_in_seconds + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + port == o.port && + expires_in_seconds == o.expires_in_seconds + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [port, expires_in_seconds].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_capacity.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_capacity.rb new file mode 100644 index 00000000..5bf9a0d9 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_capacity.rb @@ -0,0 +1,199 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxCapacity < ApiModelBase + attr_accessor :region + + attr_accessor :allocated_memory_mb + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'region' => :'region', + :'allocated_memory_mb' => :'allocated_memory_mb' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'region' => :'String', + :'allocated_memory_mb' => :'Integer' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxCapacity` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxCapacity`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'region') + self.region = attributes[:'region'] + else + self.region = nil + end + + if attributes.key?(:'allocated_memory_mb') + self.allocated_memory_mb = attributes[:'allocated_memory_mb'] + else + self.allocated_memory_mb = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @region.nil? + invalid_properties.push('invalid value for "region", region cannot be nil.') + end + + if @allocated_memory_mb.nil? + invalid_properties.push('invalid value for "allocated_memory_mb", allocated_memory_mb cannot be nil.') + end + + if @allocated_memory_mb < 0 + invalid_properties.push('invalid value for "allocated_memory_mb", must be greater than or equal to 0.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @region.nil? + return false if @allocated_memory_mb.nil? + return false if @allocated_memory_mb < 0 + true + end + + # Custom attribute writer method with validation + # @param [Object] region Value to be assigned + def region=(region) + if region.nil? + fail ArgumentError, 'region cannot be nil' + end + + @region = region + end + + # Custom attribute writer method with validation + # @param [Object] allocated_memory_mb Value to be assigned + def allocated_memory_mb=(allocated_memory_mb) + if allocated_memory_mb.nil? + fail ArgumentError, 'allocated_memory_mb cannot be nil' + end + + if allocated_memory_mb < 0 + fail ArgumentError, 'invalid value for "allocated_memory_mb", must be greater than or equal to 0.' + end + + @allocated_memory_mb = allocated_memory_mb + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + region == o.region && + allocated_memory_mb == o.allocated_memory_mb + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [region, allocated_memory_mb].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_capacity_list.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_capacity_list.rb new file mode 100644 index 00000000..679c1a05 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_capacity_list.rb @@ -0,0 +1,166 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxCapacityList < ApiModelBase + attr_accessor :data + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'data' => :'data' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'data' => :'Array' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxCapacityList` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxCapacityList`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'data') + if (value = attributes[:'data']).is_a?(Array) + self.data = value + end + else + self.data = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @data.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + @data = data + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + data == o.data + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [data].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_command_request.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_command_request.rb new file mode 100644 index 00000000..8462a7b7 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_command_request.rb @@ -0,0 +1,251 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxCommandRequest < ApiModelBase + attr_accessor :command + + attr_accessor :timeout_seconds + + attr_accessor :environment + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'command' => :'command', + :'timeout_seconds' => :'timeout_seconds', + :'environment' => :'environment' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'command' => :'String', + :'timeout_seconds' => :'Integer', + :'environment' => :'Hash' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxCommandRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxCommandRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'command') + self.command = attributes[:'command'] + else + self.command = nil + end + + if attributes.key?(:'timeout_seconds') + self.timeout_seconds = attributes[:'timeout_seconds'] + else + self.timeout_seconds = 60 + end + + if attributes.key?(:'environment') + if (value = attributes[:'environment']).is_a?(Hash) + self.environment = value + end + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @command.nil? + invalid_properties.push('invalid value for "command", command cannot be nil.') + end + + if @command.to_s.length > 65536 + invalid_properties.push('invalid value for "command", the character length must be smaller than or equal to 65536.') + end + + if @command.to_s.length < 1 + invalid_properties.push('invalid value for "command", the character length must be greater than or equal to 1.') + end + + if !@timeout_seconds.nil? && @timeout_seconds > 3600 + invalid_properties.push('invalid value for "timeout_seconds", must be smaller than or equal to 3600.') + end + + if !@timeout_seconds.nil? && @timeout_seconds < 1 + invalid_properties.push('invalid value for "timeout_seconds", must be greater than or equal to 1.') + end + + if !@environment.nil? && @environment.length > 64 + invalid_properties.push('invalid value for "environment", number of items must be less than or equal to 64.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @command.nil? + return false if @command.to_s.length > 65536 + return false if @command.to_s.length < 1 + return false if !@timeout_seconds.nil? && @timeout_seconds > 3600 + return false if !@timeout_seconds.nil? && @timeout_seconds < 1 + return false if !@environment.nil? && @environment.length > 64 + true + end + + # Custom attribute writer method with validation + # @param [Object] command Value to be assigned + def command=(command) + if command.nil? + fail ArgumentError, 'command cannot be nil' + end + + if command.to_s.length > 65536 + fail ArgumentError, 'invalid value for "command", the character length must be smaller than or equal to 65536.' + end + + if command.to_s.length < 1 + fail ArgumentError, 'invalid value for "command", the character length must be greater than or equal to 1.' + end + + @command = command + end + + # Custom attribute writer method with validation + # @param [Object] timeout_seconds Value to be assigned + def timeout_seconds=(timeout_seconds) + if timeout_seconds.nil? + fail ArgumentError, 'timeout_seconds cannot be nil' + end + + if timeout_seconds > 3600 + fail ArgumentError, 'invalid value for "timeout_seconds", must be smaller than or equal to 3600.' + end + + if timeout_seconds < 1 + fail ArgumentError, 'invalid value for "timeout_seconds", must be greater than or equal to 1.' + end + + @timeout_seconds = timeout_seconds + end + + # Custom attribute writer method with validation + # @param [Object] environment Value to be assigned + def environment=(environment) + if environment.nil? + fail ArgumentError, 'environment cannot be nil' + end + + if environment.length > 64 + fail ArgumentError, 'invalid value for "environment", number of items must be less than or equal to 64.' + end + + @environment = environment + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + command == o.command && + timeout_seconds == o.timeout_seconds && + environment == o.environment + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [command, timeout_seconds, environment].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_command_result.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_command_result.rb new file mode 100644 index 00000000..dffe0ebe --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_command_result.rb @@ -0,0 +1,294 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxCommandResult < ApiModelBase + attr_accessor :stdout + + attr_accessor :stderr + + attr_accessor :exit_code + + attr_accessor :stdout_truncated + + attr_accessor :stderr_truncated + + attr_accessor :timed_out + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'stdout' => :'stdout', + :'stderr' => :'stderr', + :'exit_code' => :'exit_code', + :'stdout_truncated' => :'stdout_truncated', + :'stderr_truncated' => :'stderr_truncated', + :'timed_out' => :'timed_out' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'stdout' => :'String', + :'stderr' => :'String', + :'exit_code' => :'Integer', + :'stdout_truncated' => :'Boolean', + :'stderr_truncated' => :'Boolean', + :'timed_out' => :'Boolean' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxCommandResult` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxCommandResult`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'stdout') + self.stdout = attributes[:'stdout'] + else + self.stdout = nil + end + + if attributes.key?(:'stderr') + self.stderr = attributes[:'stderr'] + else + self.stderr = nil + end + + if attributes.key?(:'exit_code') + self.exit_code = attributes[:'exit_code'] + else + self.exit_code = nil + end + + if attributes.key?(:'stdout_truncated') + self.stdout_truncated = attributes[:'stdout_truncated'] + else + self.stdout_truncated = nil + end + + if attributes.key?(:'stderr_truncated') + self.stderr_truncated = attributes[:'stderr_truncated'] + else + self.stderr_truncated = nil + end + + if attributes.key?(:'timed_out') + self.timed_out = attributes[:'timed_out'] + else + self.timed_out = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @stdout.nil? + invalid_properties.push('invalid value for "stdout", stdout cannot be nil.') + end + + if @stderr.nil? + invalid_properties.push('invalid value for "stderr", stderr cannot be nil.') + end + + if @exit_code.nil? + invalid_properties.push('invalid value for "exit_code", exit_code cannot be nil.') + end + + if @stdout_truncated.nil? + invalid_properties.push('invalid value for "stdout_truncated", stdout_truncated cannot be nil.') + end + + if @stderr_truncated.nil? + invalid_properties.push('invalid value for "stderr_truncated", stderr_truncated cannot be nil.') + end + + if @timed_out.nil? + invalid_properties.push('invalid value for "timed_out", timed_out cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @stdout.nil? + return false if @stderr.nil? + return false if @exit_code.nil? + return false if @stdout_truncated.nil? + return false if @stderr_truncated.nil? + return false if @timed_out.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] stdout Value to be assigned + def stdout=(stdout) + if stdout.nil? + fail ArgumentError, 'stdout cannot be nil' + end + + @stdout = stdout + end + + # Custom attribute writer method with validation + # @param [Object] stderr Value to be assigned + def stderr=(stderr) + if stderr.nil? + fail ArgumentError, 'stderr cannot be nil' + end + + @stderr = stderr + end + + # Custom attribute writer method with validation + # @param [Object] exit_code Value to be assigned + def exit_code=(exit_code) + if exit_code.nil? + fail ArgumentError, 'exit_code cannot be nil' + end + + @exit_code = exit_code + end + + # Custom attribute writer method with validation + # @param [Object] stdout_truncated Value to be assigned + def stdout_truncated=(stdout_truncated) + if stdout_truncated.nil? + fail ArgumentError, 'stdout_truncated cannot be nil' + end + + @stdout_truncated = stdout_truncated + end + + # Custom attribute writer method with validation + # @param [Object] stderr_truncated Value to be assigned + def stderr_truncated=(stderr_truncated) + if stderr_truncated.nil? + fail ArgumentError, 'stderr_truncated cannot be nil' + end + + @stderr_truncated = stderr_truncated + end + + # Custom attribute writer method with validation + # @param [Object] timed_out Value to be assigned + def timed_out=(timed_out) + if timed_out.nil? + fail ArgumentError, 'timed_out cannot be nil' + end + + @timed_out = timed_out + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + stdout == o.stdout && + stderr == o.stderr && + exit_code == o.exit_code && + stdout_truncated == o.stdout_truncated && + stderr_truncated == o.stderr_truncated && + timed_out == o.timed_out + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [stdout, stderr, exit_code, stdout_truncated, stderr_truncated, timed_out].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_deployment.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_deployment.rb new file mode 100644 index 00000000..6e7aefb2 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_deployment.rb @@ -0,0 +1,242 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxDeployment < ApiModelBase + attr_accessor :id + + attr_accessor :status + + attr_accessor :created_at + + attr_accessor :updated_at + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'id' => :'id', + :'status' => :'status', + :'created_at' => :'created_at', + :'updated_at' => :'updated_at' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'id' => :'String', + :'status' => :'String', + :'created_at' => :'Time', + :'updated_at' => :'Time' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxDeployment` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxDeployment`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'status') + self.status = attributes[:'status'] + else + self.status = nil + end + + if attributes.key?(:'created_at') + self.created_at = attributes[:'created_at'] + else + self.created_at = nil + end + + if attributes.key?(:'updated_at') + self.updated_at = attributes[:'updated_at'] + else + self.updated_at = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @id.nil? + invalid_properties.push('invalid value for "id", id cannot be nil.') + end + + if @status.nil? + invalid_properties.push('invalid value for "status", status cannot be nil.') + end + + if @created_at.nil? + invalid_properties.push('invalid value for "created_at", created_at cannot be nil.') + end + + if @updated_at.nil? + invalid_properties.push('invalid value for "updated_at", updated_at cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @id.nil? + return false if @status.nil? + return false if @created_at.nil? + return false if @updated_at.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] id Value to be assigned + def id=(id) + if id.nil? + fail ArgumentError, 'id cannot be nil' + end + + @id = id + end + + # Custom attribute writer method with validation + # @param [Object] status Value to be assigned + def status=(status) + if status.nil? + fail ArgumentError, 'status cannot be nil' + end + + @status = status + end + + # Custom attribute writer method with validation + # @param [Object] created_at Value to be assigned + def created_at=(created_at) + if created_at.nil? + fail ArgumentError, 'created_at cannot be nil' + end + + @created_at = created_at + end + + # Custom attribute writer method with validation + # @param [Object] updated_at Value to be assigned + def updated_at=(updated_at) + if updated_at.nil? + fail ArgumentError, 'updated_at cannot be nil' + end + + @updated_at = updated_at + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + status == o.status && + created_at == o.created_at && + updated_at == o.updated_at + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, status, created_at, updated_at].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_deployment_page.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_deployment_page.rb new file mode 100644 index 00000000..31a9afaf --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_deployment_page.rb @@ -0,0 +1,192 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxDeploymentPage < ApiModelBase + attr_accessor :data + + attr_accessor :pagination + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'data' => :'data', + :'pagination' => :'pagination' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'data' => :'Array', + :'pagination' => :'SandboxPagination' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxDeploymentPage` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxDeploymentPage`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'data') + if (value = attributes[:'data']).is_a?(Array) + self.data = value + end + else + self.data = nil + end + + if attributes.key?(:'pagination') + self.pagination = attributes[:'pagination'] + else + self.pagination = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + if @pagination.nil? + invalid_properties.push('invalid value for "pagination", pagination cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @data.nil? + return false if @pagination.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + @data = data + end + + # Custom attribute writer method with validation + # @param [Object] pagination Value to be assigned + def pagination=(pagination) + if pagination.nil? + fail ArgumentError, 'pagination cannot be nil' + end + + @pagination = pagination + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + data == o.data && + pagination == o.pagination + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [data, pagination].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_execution_request.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_execution_request.rb new file mode 100644 index 00000000..597db398 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_execution_request.rb @@ -0,0 +1,339 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxExecutionRequest < ApiModelBase + attr_accessor :preset + + attr_accessor :sandbox_id + + attr_accessor :memory_mb + + attr_accessor :region + + attr_accessor :command + + attr_accessor :timeout_seconds + + attr_accessor :environment + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'preset' => :'preset', + :'sandbox_id' => :'sandbox_id', + :'memory_mb' => :'memory_mb', + :'region' => :'region', + :'command' => :'command', + :'timeout_seconds' => :'timeout_seconds', + :'environment' => :'environment' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'preset' => :'String', + :'sandbox_id' => :'String', + :'memory_mb' => :'Integer', + :'region' => :'String', + :'command' => :'String', + :'timeout_seconds' => :'Integer', + :'environment' => :'Hash' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxExecutionRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxExecutionRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'preset') + self.preset = attributes[:'preset'] + end + + if attributes.key?(:'sandbox_id') + self.sandbox_id = attributes[:'sandbox_id'] + end + + if attributes.key?(:'memory_mb') + self.memory_mb = attributes[:'memory_mb'] + end + + if attributes.key?(:'region') + self.region = attributes[:'region'] + else + self.region = nil + end + + if attributes.key?(:'command') + self.command = attributes[:'command'] + else + self.command = nil + end + + if attributes.key?(:'timeout_seconds') + self.timeout_seconds = attributes[:'timeout_seconds'] + else + self.timeout_seconds = 60 + end + + if attributes.key?(:'environment') + if (value = attributes[:'environment']).is_a?(Hash) + self.environment = value + end + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @region.nil? + invalid_properties.push('invalid value for "region", region cannot be nil.') + end + + pattern = Regexp.new(/^aws-[a-z0-9-]+$/) + if @region !~ pattern + invalid_properties.push("invalid value for \"region\", must conform to the pattern #{pattern}.") + end + + if @command.nil? + invalid_properties.push('invalid value for "command", command cannot be nil.') + end + + if @command.to_s.length > 65536 + invalid_properties.push('invalid value for "command", the character length must be smaller than or equal to 65536.') + end + + if @command.to_s.length < 1 + invalid_properties.push('invalid value for "command", the character length must be greater than or equal to 1.') + end + + if !@timeout_seconds.nil? && @timeout_seconds > 60 + invalid_properties.push('invalid value for "timeout_seconds", must be smaller than or equal to 60.') + end + + if !@timeout_seconds.nil? && @timeout_seconds < 1 + invalid_properties.push('invalid value for "timeout_seconds", must be greater than or equal to 1.') + end + + if !@environment.nil? && @environment.length > 64 + invalid_properties.push('invalid value for "environment", number of items must be less than or equal to 64.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + preset_validator = EnumAttributeValidator.new('String', ["python3.12", "node22"]) + return false unless preset_validator.valid?(@preset) + memory_mb_validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + return false unless memory_mb_validator.valid?(@memory_mb) + return false if @region.nil? + return false if @region !~ Regexp.new(/^aws-[a-z0-9-]+$/) + return false if @command.nil? + return false if @command.to_s.length > 65536 + return false if @command.to_s.length < 1 + return false if !@timeout_seconds.nil? && @timeout_seconds > 60 + return false if !@timeout_seconds.nil? && @timeout_seconds < 1 + return false if !@environment.nil? && @environment.length > 64 + true + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] preset Object to be assigned + def preset=(preset) + validator = EnumAttributeValidator.new('String', ["python3.12", "node22"]) + unless validator.valid?(preset) + fail ArgumentError, "invalid value for \"preset\", must be one of #{validator.allowable_values}." + end + @preset = preset + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] memory_mb Object to be assigned + def memory_mb=(memory_mb) + validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + unless validator.valid?(memory_mb) + fail ArgumentError, "invalid value for \"memory_mb\", must be one of #{validator.allowable_values}." + end + @memory_mb = memory_mb + end + + # Custom attribute writer method with validation + # @param [Object] region Value to be assigned + def region=(region) + if region.nil? + fail ArgumentError, 'region cannot be nil' + end + + pattern = Regexp.new(/^aws-[a-z0-9-]+$/) + if region !~ pattern + fail ArgumentError, "invalid value for \"region\", must conform to the pattern #{pattern}." + end + + @region = region + end + + # Custom attribute writer method with validation + # @param [Object] command Value to be assigned + def command=(command) + if command.nil? + fail ArgumentError, 'command cannot be nil' + end + + if command.to_s.length > 65536 + fail ArgumentError, 'invalid value for "command", the character length must be smaller than or equal to 65536.' + end + + if command.to_s.length < 1 + fail ArgumentError, 'invalid value for "command", the character length must be greater than or equal to 1.' + end + + @command = command + end + + # Custom attribute writer method with validation + # @param [Object] timeout_seconds Value to be assigned + def timeout_seconds=(timeout_seconds) + if timeout_seconds.nil? + fail ArgumentError, 'timeout_seconds cannot be nil' + end + + if timeout_seconds > 60 + fail ArgumentError, 'invalid value for "timeout_seconds", must be smaller than or equal to 60.' + end + + if timeout_seconds < 1 + fail ArgumentError, 'invalid value for "timeout_seconds", must be greater than or equal to 1.' + end + + @timeout_seconds = timeout_seconds + end + + # Custom attribute writer method with validation + # @param [Object] environment Value to be assigned + def environment=(environment) + if environment.nil? + fail ArgumentError, 'environment cannot be nil' + end + + if environment.length > 64 + fail ArgumentError, 'invalid value for "environment", number of items must be less than or equal to 64.' + end + + @environment = environment + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + preset == o.preset && + sandbox_id == o.sandbox_id && + memory_mb == o.memory_mb && + region == o.region && + command == o.command && + timeout_seconds == o.timeout_seconds && + environment == o.environment + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [preset, sandbox_id, memory_mb, region, command, timeout_seconds, environment].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_execution_result.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_execution_result.rb new file mode 100644 index 00000000..2c2bd216 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_execution_result.rb @@ -0,0 +1,381 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxExecutionResult < ApiModelBase + attr_accessor :stdout + + attr_accessor :stderr + + attr_accessor :exit_code + + attr_accessor :stdout_truncated + + attr_accessor :stderr_truncated + + attr_accessor :timed_out + + attr_accessor :session_id + + attr_accessor :region + + attr_accessor :duration_ms + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'stdout' => :'stdout', + :'stderr' => :'stderr', + :'exit_code' => :'exit_code', + :'stdout_truncated' => :'stdout_truncated', + :'stderr_truncated' => :'stderr_truncated', + :'timed_out' => :'timed_out', + :'session_id' => :'session_id', + :'region' => :'region', + :'duration_ms' => :'duration_ms' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'stdout' => :'String', + :'stderr' => :'String', + :'exit_code' => :'Integer', + :'stdout_truncated' => :'Boolean', + :'stderr_truncated' => :'Boolean', + :'timed_out' => :'Boolean', + :'session_id' => :'String', + :'region' => :'String', + :'duration_ms' => :'Integer' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxExecutionResult` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxExecutionResult`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'stdout') + self.stdout = attributes[:'stdout'] + else + self.stdout = nil + end + + if attributes.key?(:'stderr') + self.stderr = attributes[:'stderr'] + else + self.stderr = nil + end + + if attributes.key?(:'exit_code') + self.exit_code = attributes[:'exit_code'] + else + self.exit_code = nil + end + + if attributes.key?(:'stdout_truncated') + self.stdout_truncated = attributes[:'stdout_truncated'] + else + self.stdout_truncated = nil + end + + if attributes.key?(:'stderr_truncated') + self.stderr_truncated = attributes[:'stderr_truncated'] + else + self.stderr_truncated = nil + end + + if attributes.key?(:'timed_out') + self.timed_out = attributes[:'timed_out'] + else + self.timed_out = nil + end + + if attributes.key?(:'session_id') + self.session_id = attributes[:'session_id'] + else + self.session_id = nil + end + + if attributes.key?(:'region') + self.region = attributes[:'region'] + else + self.region = nil + end + + if attributes.key?(:'duration_ms') + self.duration_ms = attributes[:'duration_ms'] + else + self.duration_ms = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @stdout.nil? + invalid_properties.push('invalid value for "stdout", stdout cannot be nil.') + end + + if @stderr.nil? + invalid_properties.push('invalid value for "stderr", stderr cannot be nil.') + end + + if @exit_code.nil? + invalid_properties.push('invalid value for "exit_code", exit_code cannot be nil.') + end + + if @stdout_truncated.nil? + invalid_properties.push('invalid value for "stdout_truncated", stdout_truncated cannot be nil.') + end + + if @stderr_truncated.nil? + invalid_properties.push('invalid value for "stderr_truncated", stderr_truncated cannot be nil.') + end + + if @timed_out.nil? + invalid_properties.push('invalid value for "timed_out", timed_out cannot be nil.') + end + + if @session_id.nil? + invalid_properties.push('invalid value for "session_id", session_id cannot be nil.') + end + + if @region.nil? + invalid_properties.push('invalid value for "region", region cannot be nil.') + end + + if @duration_ms.nil? + invalid_properties.push('invalid value for "duration_ms", duration_ms cannot be nil.') + end + + if @duration_ms < 0 + invalid_properties.push('invalid value for "duration_ms", must be greater than or equal to 0.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @stdout.nil? + return false if @stderr.nil? + return false if @exit_code.nil? + return false if @stdout_truncated.nil? + return false if @stderr_truncated.nil? + return false if @timed_out.nil? + return false if @session_id.nil? + return false if @region.nil? + return false if @duration_ms.nil? + return false if @duration_ms < 0 + true + end + + # Custom attribute writer method with validation + # @param [Object] stdout Value to be assigned + def stdout=(stdout) + if stdout.nil? + fail ArgumentError, 'stdout cannot be nil' + end + + @stdout = stdout + end + + # Custom attribute writer method with validation + # @param [Object] stderr Value to be assigned + def stderr=(stderr) + if stderr.nil? + fail ArgumentError, 'stderr cannot be nil' + end + + @stderr = stderr + end + + # Custom attribute writer method with validation + # @param [Object] exit_code Value to be assigned + def exit_code=(exit_code) + if exit_code.nil? + fail ArgumentError, 'exit_code cannot be nil' + end + + @exit_code = exit_code + end + + # Custom attribute writer method with validation + # @param [Object] stdout_truncated Value to be assigned + def stdout_truncated=(stdout_truncated) + if stdout_truncated.nil? + fail ArgumentError, 'stdout_truncated cannot be nil' + end + + @stdout_truncated = stdout_truncated + end + + # Custom attribute writer method with validation + # @param [Object] stderr_truncated Value to be assigned + def stderr_truncated=(stderr_truncated) + if stderr_truncated.nil? + fail ArgumentError, 'stderr_truncated cannot be nil' + end + + @stderr_truncated = stderr_truncated + end + + # Custom attribute writer method with validation + # @param [Object] timed_out Value to be assigned + def timed_out=(timed_out) + if timed_out.nil? + fail ArgumentError, 'timed_out cannot be nil' + end + + @timed_out = timed_out + end + + # Custom attribute writer method with validation + # @param [Object] session_id Value to be assigned + def session_id=(session_id) + if session_id.nil? + fail ArgumentError, 'session_id cannot be nil' + end + + @session_id = session_id + end + + # Custom attribute writer method with validation + # @param [Object] region Value to be assigned + def region=(region) + if region.nil? + fail ArgumentError, 'region cannot be nil' + end + + @region = region + end + + # Custom attribute writer method with validation + # @param [Object] duration_ms Value to be assigned + def duration_ms=(duration_ms) + if duration_ms.nil? + fail ArgumentError, 'duration_ms cannot be nil' + end + + if duration_ms < 0 + fail ArgumentError, 'invalid value for "duration_ms", must be greater than or equal to 0.' + end + + @duration_ms = duration_ms + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + stdout == o.stdout && + stderr == o.stderr && + exit_code == o.exit_code && + stdout_truncated == o.stdout_truncated && + stderr_truncated == o.stderr_truncated && + timed_out == o.timed_out && + session_id == o.session_id && + region == o.region && + duration_ms == o.duration_ms + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [stdout, stderr, exit_code, stdout_truncated, stderr_truncated, timed_out, session_id, region, duration_ms].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_read_request.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_read_request.rb new file mode 100644 index 00000000..5323ce7f --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_read_request.rb @@ -0,0 +1,182 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxFileReadRequest < ApiModelBase + attr_accessor :path + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'path' => :'path' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'path' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxFileReadRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxFileReadRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'path') + self.path = attributes[:'path'] + else + self.path = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @path.nil? + invalid_properties.push('invalid value for "path", path cannot be nil.') + end + + if @path.to_s.length > 4096 + invalid_properties.push('invalid value for "path", the character length must be smaller than or equal to 4096.') + end + + if @path.to_s.length < 1 + invalid_properties.push('invalid value for "path", the character length must be greater than or equal to 1.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @path.nil? + return false if @path.to_s.length > 4096 + return false if @path.to_s.length < 1 + true + end + + # Custom attribute writer method with validation + # @param [Object] path Value to be assigned + def path=(path) + if path.nil? + fail ArgumentError, 'path cannot be nil' + end + + if path.to_s.length > 4096 + fail ArgumentError, 'invalid value for "path", the character length must be smaller than or equal to 4096.' + end + + if path.to_s.length < 1 + fail ArgumentError, 'invalid value for "path", the character length must be greater than or equal to 1.' + end + + @path = path + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + path == o.path + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [path].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_result.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_result.rb new file mode 100644 index 00000000..18af717c --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_result.rb @@ -0,0 +1,164 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxFileResult < ApiModelBase + attr_accessor :data + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'data' => :'data' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'data' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxFileResult` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxFileResult`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'data') + self.data = attributes[:'data'] + else + self.data = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @data.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + @data = data + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + data == o.data + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [data].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_write_request.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_write_request.rb new file mode 100644 index 00000000..67f64edc --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_file_write_request.rb @@ -0,0 +1,217 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxFileWriteRequest < ApiModelBase + attr_accessor :path + + attr_accessor :data + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'path' => :'path', + :'data' => :'data' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'path' => :'String', + :'data' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxFileWriteRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxFileWriteRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'path') + self.path = attributes[:'path'] + else + self.path = nil + end + + if attributes.key?(:'data') + self.data = attributes[:'data'] + else + self.data = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @path.nil? + invalid_properties.push('invalid value for "path", path cannot be nil.') + end + + if @path.to_s.length > 4096 + invalid_properties.push('invalid value for "path", the character length must be smaller than or equal to 4096.') + end + + if @path.to_s.length < 1 + invalid_properties.push('invalid value for "path", the character length must be greater than or equal to 1.') + end + + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + if @data.to_s.length > 11184812 + invalid_properties.push('invalid value for "data", the character length must be smaller than or equal to 11184812.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @path.nil? + return false if @path.to_s.length > 4096 + return false if @path.to_s.length < 1 + return false if @data.nil? + return false if @data.to_s.length > 11184812 + true + end + + # Custom attribute writer method with validation + # @param [Object] path Value to be assigned + def path=(path) + if path.nil? + fail ArgumentError, 'path cannot be nil' + end + + if path.to_s.length > 4096 + fail ArgumentError, 'invalid value for "path", the character length must be smaller than or equal to 4096.' + end + + if path.to_s.length < 1 + fail ArgumentError, 'invalid value for "path", the character length must be greater than or equal to 1.' + end + + @path = path + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + if data.to_s.length > 11184812 + fail ArgumentError, 'invalid value for "data", the character length must be smaller than or equal to 11184812.' + end + + @data = data + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + path == o.path && + data == o.data + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [path, data].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_pagination.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_pagination.rb new file mode 100644 index 00000000..9a40e575 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_pagination.rb @@ -0,0 +1,199 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxPagination < ApiModelBase + attr_accessor :limit + + attr_accessor :has_more + + attr_accessor :next_cursor + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'limit' => :'limit', + :'has_more' => :'has_more', + :'next_cursor' => :'next_cursor' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'limit' => :'Integer', + :'has_more' => :'Boolean', + :'next_cursor' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxPagination` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxPagination`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'limit') + self.limit = attributes[:'limit'] + else + self.limit = nil + end + + if attributes.key?(:'has_more') + self.has_more = attributes[:'has_more'] + else + self.has_more = nil + end + + if attributes.key?(:'next_cursor') + self.next_cursor = attributes[:'next_cursor'] + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @limit.nil? + invalid_properties.push('invalid value for "limit", limit cannot be nil.') + end + + if @has_more.nil? + invalid_properties.push('invalid value for "has_more", has_more cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @limit.nil? + return false if @has_more.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] limit Value to be assigned + def limit=(limit) + if limit.nil? + fail ArgumentError, 'limit cannot be nil' + end + + @limit = limit + end + + # Custom attribute writer method with validation + # @param [Object] has_more Value to be assigned + def has_more=(has_more) + if has_more.nil? + fail ArgumentError, 'has_more cannot be nil' + end + + @has_more = has_more + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + limit == o.limit && + has_more == o.has_more && + next_cursor == o.next_cursor + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [limit, has_more, next_cursor].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_preset.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_preset.rb new file mode 100644 index 00000000..91316a27 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_preset.rb @@ -0,0 +1,294 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxPreset < ApiModelBase + attr_accessor :id + + attr_accessor :runtime + + attr_accessor :version + + attr_accessor :memory_mb + + attr_accessor :regions + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'id' => :'id', + :'runtime' => :'runtime', + :'version' => :'version', + :'memory_mb' => :'memory_mb', + :'regions' => :'regions' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'id' => :'String', + :'runtime' => :'String', + :'version' => :'String', + :'memory_mb' => :'Integer', + :'regions' => :'Array' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxPreset` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxPreset`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'runtime') + self.runtime = attributes[:'runtime'] + else + self.runtime = nil + end + + if attributes.key?(:'version') + self.version = attributes[:'version'] + else + self.version = nil + end + + if attributes.key?(:'memory_mb') + self.memory_mb = attributes[:'memory_mb'] + else + self.memory_mb = nil + end + + if attributes.key?(:'regions') + if (value = attributes[:'regions']).is_a?(Array) + self.regions = value + end + else + self.regions = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @id.nil? + invalid_properties.push('invalid value for "id", id cannot be nil.') + end + + if @runtime.nil? + invalid_properties.push('invalid value for "runtime", runtime cannot be nil.') + end + + if @version.nil? + invalid_properties.push('invalid value for "version", version cannot be nil.') + end + + if @memory_mb.nil? + invalid_properties.push('invalid value for "memory_mb", memory_mb cannot be nil.') + end + + if @regions.nil? + invalid_properties.push('invalid value for "regions", regions cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @id.nil? + return false if @runtime.nil? + return false if @version.nil? + return false if @memory_mb.nil? + memory_mb_validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + return false unless memory_mb_validator.valid?(@memory_mb) + return false if @regions.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] id Value to be assigned + def id=(id) + if id.nil? + fail ArgumentError, 'id cannot be nil' + end + + @id = id + end + + # Custom attribute writer method with validation + # @param [Object] runtime Value to be assigned + def runtime=(runtime) + if runtime.nil? + fail ArgumentError, 'runtime cannot be nil' + end + + @runtime = runtime + end + + # Custom attribute writer method with validation + # @param [Object] version Value to be assigned + def version=(version) + if version.nil? + fail ArgumentError, 'version cannot be nil' + end + + @version = version + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] memory_mb Object to be assigned + def memory_mb=(memory_mb) + validator = EnumAttributeValidator.new('Integer', [1024, 2048]) + unless validator.valid?(memory_mb) + fail ArgumentError, "invalid value for \"memory_mb\", must be one of #{validator.allowable_values}." + end + @memory_mb = memory_mb + end + + # Custom attribute writer method with validation + # @param [Object] regions Value to be assigned + def regions=(regions) + if regions.nil? + fail ArgumentError, 'regions cannot be nil' + end + + @regions = regions + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + runtime == o.runtime && + version == o.version && + memory_mb == o.memory_mb && + regions == o.regions + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, runtime, version, memory_mb, regions].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_preset_list.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_preset_list.rb new file mode 100644 index 00000000..b2e12396 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_preset_list.rb @@ -0,0 +1,166 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxPresetList < ApiModelBase + attr_accessor :data + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'data' => :'data' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'data' => :'Array' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxPresetList` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxPresetList`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'data') + if (value = attributes[:'data']).is_a?(Array) + self.data = value + end + else + self.data = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @data.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + @data = data + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + data == o.data + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [data].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_session.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_session.rb new file mode 100644 index 00000000..42cafbc0 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_session.rb @@ -0,0 +1,407 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxSession < ApiModelBase + attr_accessor :id + + attr_accessor :project_id + + attr_accessor :sandbox_id + + attr_accessor :state + + attr_accessor :desired_state + + attr_accessor :region + + attr_accessor :memory_mb + + attr_accessor :created_at + + attr_accessor :started_at + + attr_accessor :expires_at + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'id' => :'id', + :'project_id' => :'project_id', + :'sandbox_id' => :'sandbox_id', + :'state' => :'state', + :'desired_state' => :'desired_state', + :'region' => :'region', + :'memory_mb' => :'memory_mb', + :'created_at' => :'created_at', + :'started_at' => :'started_at', + :'expires_at' => :'expires_at' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'id' => :'String', + :'project_id' => :'String', + :'sandbox_id' => :'String', + :'state' => :'String', + :'desired_state' => :'String', + :'region' => :'String', + :'memory_mb' => :'Integer', + :'created_at' => :'Time', + :'started_at' => :'Time', + :'expires_at' => :'Time' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxSession` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxSession`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'project_id') + self.project_id = attributes[:'project_id'] + else + self.project_id = nil + end + + if attributes.key?(:'sandbox_id') + self.sandbox_id = attributes[:'sandbox_id'] + else + self.sandbox_id = nil + end + + if attributes.key?(:'state') + self.state = attributes[:'state'] + else + self.state = nil + end + + if attributes.key?(:'desired_state') + self.desired_state = attributes[:'desired_state'] + else + self.desired_state = nil + end + + if attributes.key?(:'region') + self.region = attributes[:'region'] + else + self.region = nil + end + + if attributes.key?(:'memory_mb') + self.memory_mb = attributes[:'memory_mb'] + else + self.memory_mb = nil + end + + if attributes.key?(:'created_at') + self.created_at = attributes[:'created_at'] + else + self.created_at = nil + end + + if attributes.key?(:'started_at') + self.started_at = attributes[:'started_at'] + end + + if attributes.key?(:'expires_at') + self.expires_at = attributes[:'expires_at'] + else + self.expires_at = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @id.nil? + invalid_properties.push('invalid value for "id", id cannot be nil.') + end + + if @project_id.nil? + invalid_properties.push('invalid value for "project_id", project_id cannot be nil.') + end + + if @sandbox_id.nil? + invalid_properties.push('invalid value for "sandbox_id", sandbox_id cannot be nil.') + end + + if @state.nil? + invalid_properties.push('invalid value for "state", state cannot be nil.') + end + + if @desired_state.nil? + invalid_properties.push('invalid value for "desired_state", desired_state cannot be nil.') + end + + if @region.nil? + invalid_properties.push('invalid value for "region", region cannot be nil.') + end + + if @memory_mb.nil? + invalid_properties.push('invalid value for "memory_mb", memory_mb cannot be nil.') + end + + if @created_at.nil? + invalid_properties.push('invalid value for "created_at", created_at cannot be nil.') + end + + if @expires_at.nil? + invalid_properties.push('invalid value for "expires_at", expires_at cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @id.nil? + return false if @project_id.nil? + return false if @sandbox_id.nil? + return false if @state.nil? + state_validator = EnumAttributeValidator.new('String', ["starting", "running", "suspending", "suspended", "resuming", "terminating", "terminated", "unknown"]) + return false unless state_validator.valid?(@state) + return false if @desired_state.nil? + desired_state_validator = EnumAttributeValidator.new('String', ["running", "suspended", "terminated"]) + return false unless desired_state_validator.valid?(@desired_state) + return false if @region.nil? + return false if @memory_mb.nil? + return false if @created_at.nil? + return false if @expires_at.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] id Value to be assigned + def id=(id) + if id.nil? + fail ArgumentError, 'id cannot be nil' + end + + @id = id + end + + # Custom attribute writer method with validation + # @param [Object] project_id Value to be assigned + def project_id=(project_id) + if project_id.nil? + fail ArgumentError, 'project_id cannot be nil' + end + + @project_id = project_id + end + + # Custom attribute writer method with validation + # @param [Object] sandbox_id Value to be assigned + def sandbox_id=(sandbox_id) + if sandbox_id.nil? + fail ArgumentError, 'sandbox_id cannot be nil' + end + + @sandbox_id = sandbox_id + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] state Object to be assigned + def state=(state) + validator = EnumAttributeValidator.new('String', ["starting", "running", "suspending", "suspended", "resuming", "terminating", "terminated", "unknown"]) + unless validator.valid?(state) + fail ArgumentError, "invalid value for \"state\", must be one of #{validator.allowable_values}." + end + @state = state + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] desired_state Object to be assigned + def desired_state=(desired_state) + validator = EnumAttributeValidator.new('String', ["running", "suspended", "terminated"]) + unless validator.valid?(desired_state) + fail ArgumentError, "invalid value for \"desired_state\", must be one of #{validator.allowable_values}." + end + @desired_state = desired_state + end + + # Custom attribute writer method with validation + # @param [Object] region Value to be assigned + def region=(region) + if region.nil? + fail ArgumentError, 'region cannot be nil' + end + + @region = region + end + + # Custom attribute writer method with validation + # @param [Object] memory_mb Value to be assigned + def memory_mb=(memory_mb) + if memory_mb.nil? + fail ArgumentError, 'memory_mb cannot be nil' + end + + @memory_mb = memory_mb + end + + # Custom attribute writer method with validation + # @param [Object] created_at Value to be assigned + def created_at=(created_at) + if created_at.nil? + fail ArgumentError, 'created_at cannot be nil' + end + + @created_at = created_at + end + + # Custom attribute writer method with validation + # @param [Object] expires_at Value to be assigned + def expires_at=(expires_at) + if expires_at.nil? + fail ArgumentError, 'expires_at cannot be nil' + end + + @expires_at = expires_at + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + project_id == o.project_id && + sandbox_id == o.sandbox_id && + state == o.state && + desired_state == o.desired_state && + region == o.region && + memory_mb == o.memory_mb && + created_at == o.created_at && + started_at == o.started_at && + expires_at == o.expires_at + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, project_id, sandbox_id, state, desired_state, region, memory_mb, created_at, started_at, expires_at].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_session_page.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_session_page.rb new file mode 100644 index 00000000..4f1cb30f --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_session_page.rb @@ -0,0 +1,192 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxSessionPage < ApiModelBase + attr_accessor :data + + attr_accessor :pagination + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'data' => :'data', + :'pagination' => :'pagination' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'data' => :'Array', + :'pagination' => :'SandboxPagination' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxSessionPage` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxSessionPage`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'data') + if (value = attributes[:'data']).is_a?(Array) + self.data = value + end + else + self.data = nil + end + + if attributes.key?(:'pagination') + self.pagination = attributes[:'pagination'] + else + self.pagination = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + if @pagination.nil? + invalid_properties.push('invalid value for "pagination", pagination cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @data.nil? + return false if @pagination.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + @data = data + end + + # Custom attribute writer method with validation + # @param [Object] pagination Value to be assigned + def pagination=(pagination) + if pagination.nil? + fail ArgumentError, 'pagination cannot be nil' + end + + @pagination = pagination + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + data == o.data && + pagination == o.pagination + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [data, pagination].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_subject_grant_request.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_subject_grant_request.rb new file mode 100644 index 00000000..d22a2b37 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_subject_grant_request.rb @@ -0,0 +1,164 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxSubjectGrantRequest < ApiModelBase + attr_accessor :expires_at + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'expires_at' => :'expires_at' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'expires_at' => :'Time' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxSubjectGrantRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxSubjectGrantRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'expires_at') + self.expires_at = attributes[:'expires_at'] + else + self.expires_at = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @expires_at.nil? + invalid_properties.push('invalid value for "expires_at", expires_at cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @expires_at.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] expires_at Value to be assigned + def expires_at=(expires_at) + if expires_at.nil? + fail ArgumentError, 'expires_at cannot be nil' + end + + @expires_at = expires_at + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + expires_at == o.expires_at + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [expires_at].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_template.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_template.rb new file mode 100644 index 00000000..db4e7d4f --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_template.rb @@ -0,0 +1,321 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxTemplate < ApiModelBase + attr_accessor :id + + attr_accessor :project_id + + attr_accessor :name + + attr_accessor :preset + + attr_accessor :memory_mb + + attr_accessor :status + + attr_accessor :created_at + + class EnumAttributeValidator + attr_reader :datatype + attr_reader :allowable_values + + def initialize(datatype, allowable_values) + @allowable_values = allowable_values.map do |value| + case datatype.to_s + when /Integer/i + value.to_i + when /Float/i + value.to_f + else + value + end + end + end + + def valid?(value) + !value || allowable_values.include?(value) + end + end + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'id' => :'id', + :'project_id' => :'project_id', + :'name' => :'name', + :'preset' => :'preset', + :'memory_mb' => :'memory_mb', + :'status' => :'status', + :'created_at' => :'created_at' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'id' => :'String', + :'project_id' => :'String', + :'name' => :'String', + :'preset' => :'String', + :'memory_mb' => :'Integer', + :'status' => :'String', + :'created_at' => :'Time' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxTemplate` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxTemplate`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'id') + self.id = attributes[:'id'] + else + self.id = nil + end + + if attributes.key?(:'project_id') + self.project_id = attributes[:'project_id'] + else + self.project_id = nil + end + + if attributes.key?(:'name') + self.name = attributes[:'name'] + else + self.name = nil + end + + if attributes.key?(:'preset') + self.preset = attributes[:'preset'] + end + + if attributes.key?(:'memory_mb') + self.memory_mb = attributes[:'memory_mb'] + end + + if attributes.key?(:'status') + self.status = attributes[:'status'] + else + self.status = nil + end + + if attributes.key?(:'created_at') + self.created_at = attributes[:'created_at'] + else + self.created_at = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @id.nil? + invalid_properties.push('invalid value for "id", id cannot be nil.') + end + + if @project_id.nil? + invalid_properties.push('invalid value for "project_id", project_id cannot be nil.') + end + + if @name.nil? + invalid_properties.push('invalid value for "name", name cannot be nil.') + end + + pattern = Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + if @name !~ pattern + invalid_properties.push("invalid value for \"name\", must conform to the pattern #{pattern}.") + end + + if @status.nil? + invalid_properties.push('invalid value for "status", status cannot be nil.') + end + + if @created_at.nil? + invalid_properties.push('invalid value for "created_at", created_at cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @id.nil? + return false if @project_id.nil? + return false if @name.nil? + return false if @name !~ Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + return false if @status.nil? + status_validator = EnumAttributeValidator.new('String', ["ready", "unavailable", "deleting"]) + return false unless status_validator.valid?(@status) + return false if @created_at.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] id Value to be assigned + def id=(id) + if id.nil? + fail ArgumentError, 'id cannot be nil' + end + + @id = id + end + + # Custom attribute writer method with validation + # @param [Object] project_id Value to be assigned + def project_id=(project_id) + if project_id.nil? + fail ArgumentError, 'project_id cannot be nil' + end + + @project_id = project_id + end + + # Custom attribute writer method with validation + # @param [Object] name Value to be assigned + def name=(name) + if name.nil? + fail ArgumentError, 'name cannot be nil' + end + + pattern = Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + if name !~ pattern + fail ArgumentError, "invalid value for \"name\", must conform to the pattern #{pattern}." + end + + @name = name + end + + # Custom attribute writer method checking allowed values (enum). + # @param [Object] status Object to be assigned + def status=(status) + validator = EnumAttributeValidator.new('String', ["ready", "unavailable", "deleting"]) + unless validator.valid?(status) + fail ArgumentError, "invalid value for \"status\", must be one of #{validator.allowable_values}." + end + @status = status + end + + # Custom attribute writer method with validation + # @param [Object] created_at Value to be assigned + def created_at=(created_at) + if created_at.nil? + fail ArgumentError, 'created_at cannot be nil' + end + + @created_at = created_at + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + id == o.id && + project_id == o.project_id && + name == o.name && + preset == o.preset && + memory_mb == o.memory_mb && + status == o.status && + created_at == o.created_at + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [id, project_id, name, preset, memory_mb, status, created_at].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/sandbox_template_page.rb b/lib/volcano/generated/lib/volcano-generated/models/sandbox_template_page.rb new file mode 100644 index 00000000..ad227414 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/sandbox_template_page.rb @@ -0,0 +1,192 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class SandboxTemplatePage < ApiModelBase + attr_accessor :data + + attr_accessor :pagination + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'data' => :'data', + :'pagination' => :'pagination' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'data' => :'Array', + :'pagination' => :'SandboxPagination' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::SandboxTemplatePage` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::SandboxTemplatePage`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'data') + if (value = attributes[:'data']).is_a?(Array) + self.data = value + end + else + self.data = nil + end + + if attributes.key?(:'pagination') + self.pagination = attributes[:'pagination'] + else + self.pagination = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @data.nil? + invalid_properties.push('invalid value for "data", data cannot be nil.') + end + + if @pagination.nil? + invalid_properties.push('invalid value for "pagination", pagination cannot be nil.') + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @data.nil? + return false if @pagination.nil? + true + end + + # Custom attribute writer method with validation + # @param [Object] data Value to be assigned + def data=(data) + if data.nil? + fail ArgumentError, 'data cannot be nil' + end + + @data = data + end + + # Custom attribute writer method with validation + # @param [Object] pagination Value to be assigned + def pagination=(pagination) + if pagination.nil? + fail ArgumentError, 'pagination cannot be nil' + end + + @pagination = pagination + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + data == o.data && + pagination == o.pagination + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [data, pagination].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/update_sandbox_template_request.rb b/lib/volcano/generated/lib/volcano-generated/models/update_sandbox_template_request.rb new file mode 100644 index 00000000..251eea39 --- /dev/null +++ b/lib/volcano/generated/lib/volcano-generated/models/update_sandbox_template_request.rb @@ -0,0 +1,175 @@ +=begin +#Volcano Hosting API + +#Public API for Volcano Hosting clients, SDKs, and CLI tooling (Port 8000). This specification intentionally excludes first-party/internal APIs. See api/openapi-internal.yaml for non-public internal and Builder operations. + +The version of the OpenAPI document: 3.0.0 +Contact: support@volcano.dev +Generated by: https://openapi-generator.tech +Generator version: 7.17.0 + +=end + +require 'date' +require 'time' + +module Volcano::Generated + class UpdateSandboxTemplateRequest < ApiModelBase + attr_accessor :name + + # Attribute mapping from ruby-style variable name to JSON key. + def self.attribute_map + { + :'name' => :'name' + } + end + + # Returns attribute mapping this model knows about + def self.acceptable_attribute_map + attribute_map + end + + # Returns all the JSON keys this model knows about + def self.acceptable_attributes + acceptable_attribute_map.values + end + + # Attribute type mapping. + def self.openapi_types + { + :'name' => :'String' + } + end + + # List of attributes with nullable: true + def self.openapi_nullable + Set.new([ + ]) + end + + # Initializes the object + # @param [Hash] attributes Model attributes in the form of hash + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `Volcano::Generated::UpdateSandboxTemplateRequest` initialize method" + end + + # check to see if the attribute exists and convert string to symbol for hash key + acceptable_attribute_map = self.class.acceptable_attribute_map + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!acceptable_attribute_map.key?(k.to_sym)) + fail ArgumentError, "`#{k}` is not a valid attribute in `Volcano::Generated::UpdateSandboxTemplateRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect + end + h[k.to_sym] = v + } + + if attributes.key?(:'name') + self.name = attributes[:'name'] + else + self.name = nil + end + end + + # Show invalid properties with the reasons. Usually used together with valid? + # @return Array for valid properties with the reasons + def list_invalid_properties + warn '[DEPRECATED] the `list_invalid_properties` method is obsolete' + invalid_properties = Array.new + if @name.nil? + invalid_properties.push('invalid value for "name", name cannot be nil.') + end + + pattern = Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + if @name !~ pattern + invalid_properties.push("invalid value for \"name\", must conform to the pattern #{pattern}.") + end + + invalid_properties + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + def valid? + warn '[DEPRECATED] the `valid?` method is obsolete' + return false if @name.nil? + return false if @name !~ Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + true + end + + # Custom attribute writer method with validation + # @param [Object] name Value to be assigned + def name=(name) + if name.nil? + fail ArgumentError, 'name cannot be nil' + end + + pattern = Regexp.new(/^[a-z][a-z0-9-]{0,62}$/) + if name !~ pattern + fail ArgumentError, "invalid value for \"name\", must conform to the pattern #{pattern}." + end + + @name = name + end + + # Checks equality by comparing each attribute. + # @param [Object] Object to be compared + def ==(o) + return true if self.equal?(o) + self.class == o.class && + name == o.name + end + + # @see the `==` method + # @param [Object] Object to be compared + def eql?(o) + self == o + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + def hash + [name].hash + end + + # Builds the object from hash + # @param [Hash] attributes Model attributes in the form of hash + # @return [Object] Returns the model itself + def self.build_from_hash(attributes) + return nil unless attributes.is_a?(Hash) + attributes = attributes.transform_keys(&:to_sym) + transformed_hash = {} + openapi_types.each_pair do |key, type| + if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = nil + elsif type =~ /\AArray<(.*)>/i + # check to ensure the input is an array given that the attribute + # is documented as an array but the input is not + if attributes[attribute_map[key]].is_a?(Array) + transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) } + end + elsif !attributes[attribute_map[key]].nil? + transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]]) + end + end + new(transformed_hash) + end + + # Returns the object in the form of hash + # @return [Hash] Returns the object in the form of hash + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + hash + end + + end + +end diff --git a/lib/volcano/generated/lib/volcano-generated/models/variable.rb b/lib/volcano/generated/lib/volcano-generated/models/variable.rb index 15eaa70f..95e83126 100644 --- a/lib/volcano/generated/lib/volcano-generated/models/variable.rb +++ b/lib/volcano/generated/lib/volcano-generated/models/variable.rb @@ -18,6 +18,9 @@ class Variable < ApiModelBase # Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. attr_accessor :shared + # Whether this name is in the project's shared frontend-variable list. + attr_accessor :frontend_shared + attr_accessor :id attr_accessor :project_id @@ -68,6 +71,7 @@ def valid?(value) def self.attribute_map { :'shared' => :'shared', + :'frontend_shared' => :'frontend_shared', :'id' => :'id', :'project_id' => :'project_id', :'name' => :'name', @@ -95,6 +99,7 @@ def self.acceptable_attributes def self.openapi_types { :'shared' => :'Boolean', + :'frontend_shared' => :'Boolean', :'id' => :'String', :'project_id' => :'String', :'name' => :'String', @@ -134,6 +139,10 @@ def initialize(attributes = {}) self.shared = attributes[:'shared'] end + if attributes.key?(:'frontend_shared') + self.frontend_shared = attributes[:'frontend_shared'] + end + if attributes.key?(:'id') self.id = attributes[:'id'] else @@ -331,6 +340,7 @@ def ==(o) return true if self.equal?(o) self.class == o.class && shared == o.shared && + frontend_shared == o.frontend_shared && id == o.id && project_id == o.project_id && name == o.name && @@ -352,7 +362,7 @@ def eql?(o) # Calculates hash code according to all attributes. # @return [Integer] Hash code def hash - [shared, id, project_id, name, value, status, current_sync_id, provisioning_started_at, deploy_source, created_at, updated_at].hash + [shared, frontend_shared, id, project_id, name, value, status, current_sync_id, provisioning_started_at, deploy_source, created_at, updated_at].hash end # Builds the object from hash diff --git a/lib/volcano/generated_transport.rb b/lib/volcano/generated_transport.rb index 955af48f..c5fe3fcb 100644 --- a/lib/volcano/generated_transport.rb +++ b/lib/volcano/generated_transport.rb @@ -29,6 +29,7 @@ module Volcano require_relative 'generated_transport_logs' require_relative 'generated_transport_functions' require_relative 'generated_transport_durable' + require_relative 'generated_transport_sandbox' require_relative 'generated_transport_storage' require_relative 'generated_transport_upload_sessions' diff --git a/lib/volcano/generated_transport_sandbox.rb b/lib/volcano/generated_transport_sandbox.rb new file mode 100644 index 00000000..b8d4d638 --- /dev/null +++ b/lib/volcano/generated_transport_sandbox.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Volcano + # Dispatch only the generated operations exposed by the Sandbox facade. + class GeneratedTransport + SANDBOX_OPERATIONS = { + list_sandbox_presets: lambda { |api, _request| + api.list_sandbox_presets_with_http_info(debug_return_type: 'Object') + }, + create_sandbox_session: lambda { |api, request| + api.create_sandbox_session_with_http_info(request.resource_id, request.request_id, request.body, + debug_return_type: 'Object') + }, + execute_sandbox: lambda { |api, request| + api.execute_sandbox_with_http_info(request.resource_id, request.request_id, request.body, + debug_return_type: 'Object') + }, + get_sandbox_session: lambda { |api, request| + api.get_sandbox_session_with_http_info(request.resource_id, debug_return_type: 'Object') + }, + execute_sandbox_session: lambda { |api, request| + api.execute_sandbox_session_with_http_info(request.resource_id, request.request_id, request.body, + debug_return_type: 'Object') + }, + suspend_sandbox_session: lambda { |api, request| + api.suspend_sandbox_session_with_http_info(request.resource_id, debug_return_type: 'Object') + }, + resume_sandbox_session: lambda { |api, request| + api.resume_sandbox_session_with_http_info(request.resource_id, debug_return_type: 'Object') + }, + terminate_sandbox_session: lambda { |api, request| + api.terminate_sandbox_session_with_http_info(request.resource_id, debug_return_type: 'Object') + }, + create_sandbox_session_access: lambda { |api, request| + api.create_sandbox_session_access_with_http_info(request.resource_id, request.body, debug_return_type: 'Object') + }, + read_sandbox_session_file: lambda { |api, request| + api.read_sandbox_session_file_with_http_info(request.resource_id, request.body, debug_return_type: 'Object') + }, + write_sandbox_session_file: lambda { |api, request| + api.write_sandbox_session_file_with_http_info(request.resource_id, request.body, debug_return_type: 'Object') + }, + grant_sandbox_session: lambda { |api, request| + api.grant_sandbox_session_with_http_info(request.resource_id, request.subject_id, request.body, + debug_return_type: 'Object') + }, + revoke_sandbox_session: lambda { |api, request| + api.revoke_sandbox_session_with_http_info(request.resource_id, request.subject_id, debug_return_type: 'Object') + } + }.freeze + private_constant :SANDBOX_OPERATIONS + + def sandbox_request(authorization:, request:) + invoke do + api = sandbox_api(authorization, request.timeout) + data, status, headers = SANDBOX_OPERATIONS.fetch(request.operation).call(api, request) + response(data, status, headers) + end + end + + private + + def sandbox_api(authorization, timeout) + configuration = generated_configuration(authorization) + configuration.timeout = [configuration.timeout, (timeout * 1_000).round].max + Generated::SandboxesApi.new(ApiClient.new(configuration)) + end + end +end diff --git a/lib/volcano/sandbox_files.rb b/lib/volcano/sandbox_files.rb new file mode 100644 index 00000000..ea0e9a46 --- /dev/null +++ b/lib/volcano/sandbox_files.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'base64' + +module Volcano + # Byte-preserving files inside a Sandbox session. + class SandboxFiles + FILE_LIMIT = 8 * 1024 * 1024 + private_constant :FILE_LIMIT + + def initialize(requests, session_id) + @requests = requests + @session_id = session_id + end + + def read(path) + response = @requests.call(request(:read_sandbox_session_file, { path: path })) + Base64.strict_decode64(SandboxResponse.text(Transport.json_object(response)['data'])) + end + + def write(path, data) + raise Error::ValidationError, 'Sandbox files are limited to 8 MiB' if data.bytesize > FILE_LIMIT + + body = { path: path, data: Base64.strict_encode64(data) } + @requests.call(request(:write_sandbox_session_file, body), status: 204) + nil + end + + private + + def request(operation, body) + SandboxRequest.new(operation: operation, resource_id: @session_id, body: body) + end + end +end diff --git a/lib/volcano/sandbox_models.rb b/lib/volcano/sandbox_models.rb new file mode 100644 index 00000000..6287f5ff --- /dev/null +++ b/lib/volcano/sandbox_models.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Volcano + # Command output preserves nonzero exit codes as data. + SandboxCommandResult = Data.define(:stdout, :stderr, :exit_code, :timed_out, :stdout_truncated, :stderr_truncated) + # One-shot output is returned after confirmed guest reclamation. + SandboxExecutionResult = Data.define(:stdout, :stderr, :exit_code, :timed_out, :stdout_truncated, + :stderr_truncated, :session_id, :region, :duration_ms) + # A published preset and its available memory and regions. + SandboxPreset = Data.define(:id, :memory_mb, :regions) + # Expiring HTTP access credentials are redacted from inspection. + class SandboxAccess + def initialize(url:, token:, expires_at:) + @values = { url: url.dup.freeze, token: token.dup.freeze, expires_at: expires_at.dup.freeze }.freeze + freeze + end + + def url = @values.fetch(:url) + def token = @values.fetch(:token) + def expires_at = @values.fetch(:expires_at) + + def inspect = "#" + end +end diff --git a/lib/volcano/sandbox_request.rb b/lib/volcano/sandbox_request.rb new file mode 100644 index 00000000..ae79a033 --- /dev/null +++ b/lib/volcano/sandbox_request.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'securerandom' + +module Volcano + # Immutable addressing and payload for a generated Sandbox operation. + class SandboxRequest + def initialize(options) + @options = options.dup.freeze + freeze + end + + def operation = @options.fetch(:operation) + def resource_id = @options[:resource_id] + def subject_id = @options[:subject_id] + def body = @options[:body] + def request_id = @options[:request_id] + def timeout = @options.fetch(:timeout, 180) + end + + # Shared validation and credentials for Sandbox facade operations. + class SandboxRequests + UUID = /\A[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}\z/i + USER_OPERATIONS = %i[get_sandbox_session execute_sandbox_session read_sandbox_session_file + write_sandbox_session_file create_sandbox_session_access].freeze + private_constant :UUID, :USER_OPERATIONS + + def initialize(client, transport) + @client = client + @transport = transport + end + + def call(request, status: 200) + response = if USER_OPERATIONS.include?(request.operation) && @client.current_session + @client.session_request { |token| dispatch(request, token) } + else + dispatch(request, @client.service_token) + end + Transport.body(response, status) + end + + def self.identifier(value) + return value if value.is_a?(String) && UUID.match?(value) + + raise Error::ValidationError, 'Sandbox resource and request IDs must be UUIDs' + end + + def self.request_id(options) + identifier(options.fetch(:request_id) { SecureRandom.uuid }) + end + + def self.selector(options) + if options.key?(:preset) == options.key?(:sandbox_id) + raise Error::ValidationError, 'Choose exactly one preset or sandbox_id' + end + + result = options.slice(:region, :preset, :sandbox_id, :memory_mb) + result[:sandbox_id] = identifier(result[:sandbox_id]) if result.key?(:sandbox_id) + result + end + + def self.command(command, options) + options.slice(:timeout_seconds, :environment).merge(command: command) + end + + private + + def dispatch(request, token) + Transport.invoke { @transport.sandbox_request(authorization: token, request: request) } + end + end +end diff --git a/lib/volcano/sandbox_response.rb b/lib/volcano/sandbox_response.rb new file mode 100644 index 00000000..e4658116 --- /dev/null +++ b/lib/volcano/sandbox_response.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Volcano + # Validate wire values before exposing language-native Sandbox results. + module SandboxResponse + STATES = %w[starting running suspending suspended resuming terminating terminated unknown].freeze + private_constant :STATES + + def self.text(value) + return value if value.is_a?(String) + + raise TypeError, 'Invalid Sandbox text field' + end + + def self.integer(value) + return value if value.is_a?(Integer) + + raise TypeError, 'Invalid Sandbox integer field' + end + + def self.flag(value) + return value if value.is_a?(TrueClass) || value.is_a?(FalseClass) + + raise TypeError, 'Invalid Sandbox flag' + end + + def self.state(value) + result = text(value) + return result if STATES.include?(result) + + raise TypeError, 'Invalid Sandbox state' + end + + def self.command(value) + data = Transport.json_object(value) + SandboxCommandResult.new( + stdout: text(data['stdout']), stderr: text(data['stderr']), exit_code: integer(data['exit_code']), + timed_out: flag(data['timed_out']), stdout_truncated: flag(data['stdout_truncated']), + stderr_truncated: flag(data['stderr_truncated']) + ) + end + + def self.execution(value) + data = Transport.json_object(value) + SandboxExecutionResult.new( + **command(data).to_h, session_id: text(data['session_id']), + region: text(data['region']), duration_ms: integer(data['duration_ms']) + ) + end + + def self.access(value) + data = Transport.json_object(value) + SandboxAccess.new(url: text(data['url']), token: text(data['token']), expires_at: text(data['expires_at'])) + end + + def self.preset(value) + data = Transport.json_object(value) + regions = array(data['regions']).map { |region| text(region) }.freeze + SandboxPreset.new(id: text(data['id']), memory_mb: integer(data['memory_mb']), regions: regions) + end + + def self.array(value) + return value if value.is_a?(Array) + + raise TypeError, 'Invalid Sandbox list' + end + end +end diff --git a/lib/volcano/sandbox_session.rb b/lib/volcano/sandbox_session.rb new file mode 100644 index 00000000..91865d86 --- /dev/null +++ b/lib/volcano/sandbox_session.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'English' + +module Volcano + # A session handle whose observed state changes only after validated responses. + class SandboxSession + attr_reader :id, :project_id, :region, :state, :expires_at, :files + + def initialize(requests, value) + data = Transport.json_object(value) + @requests = requests + @id = SandboxRequests.identifier(data['id']) + @project_id = SandboxRequests.identifier(data['project_id']) + @region = SandboxResponse.text(data['region']) + assign_state(data) + @files = SandboxFiles.new(requests, @id) + end + + def refresh = update(:get_sandbox_session) + def suspend = update(:suspend_sandbox_session, status: 202) + def resume = update(:resume_sandbox_session, status: 202) + def terminate = update(:terminate_sandbox_session, status: 202) + + def exec(command, options = {}) + request = SandboxRequest.new(operation: :execute_sandbox_session, resource_id: @id, + body: SandboxRequests.command(command, options), + request_id: SandboxRequests.request_id(options), + timeout: SandboxResponse.integer(options.fetch(:timeout_seconds, 60)) + 120) + SandboxResponse.command(@requests.call(request)) + end + + def access(port) + request = SandboxRequest.new(operation: :create_sandbox_session_access, resource_id: @id, body: { port: port }) + SandboxResponse.access(@requests.call(request)) + end + + def use + yield self + ensure + cleanup($ERROR_INFO) unless @state == 'terminated' + end + + private + + def cleanup(original_error) + terminate + rescue StandardError + raise unless original_error + end + + def update(operation, status: 200) + response = @requests.call(SandboxRequest.new(operation: operation, resource_id: @id), status: status) + data = Transport.json_object(response) + raise TypeError, 'Sandbox session identity changed' unless data['id'] == @id + + assign_state(data) + self + end + + def assign_state(data) + next_state = SandboxResponse.state(data['state']) + expiry = SandboxResponse.text(data['expires_at']) + @state = next_state + @expires_at = expiry + end + end +end diff --git a/lib/volcano/sandboxes.rb b/lib/volcano/sandboxes.rb new file mode 100644 index 00000000..70bc41e4 --- /dev/null +++ b/lib/volcano/sandboxes.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Volcano + # Create isolated sessions or execute a command to completion. + class Sandboxes + def initialize(client, transport) + @requests = SandboxRequests.new(client, transport) + end + + def presets + response = @requests.call(SandboxRequest.new(operation: :list_sandbox_presets)) + SandboxResponse.array(Transport.json_object(response)['data']).map { |value| SandboxResponse.preset(value) } + end + + def create(project_id, options) + body = SandboxRequests.selector(options).merge(options.slice(:max_duration_seconds, :idle_timeout_seconds)) + request = SandboxRequest.new(operation: :create_sandbox_session, + resource_id: SandboxRequests.identifier(project_id), body: body, + request_id: SandboxRequests.request_id(options)) + SandboxSession.new(@requests, @requests.call(request, status: 201)) + end + + def get(session_id) + request = SandboxRequest.new(operation: :get_sandbox_session, resource_id: SandboxRequests.identifier(session_id)) + SandboxSession.new(@requests, @requests.call(request)) + end + + def exec(project_id, command, options) + body = SandboxRequests.selector(options).merge(SandboxRequests.command(command, options)) + request = SandboxRequest.new(operation: :execute_sandbox, resource_id: SandboxRequests.identifier(project_id), + body: body, request_id: SandboxRequests.request_id(options)) + SandboxResponse.execution(@requests.call(request)) + end + + def grant(session_id, auth_user_id, expires_at:) + request = SandboxRequest.new(operation: :grant_sandbox_session, + resource_id: SandboxRequests.identifier(session_id), + subject_id: SandboxRequests.identifier(auth_user_id), + body: { expires_at: expires_at }) + @requests.call(request, status: 204) + nil + end + + def revoke(session_id, auth_user_id) + request = SandboxRequest.new(operation: :revoke_sandbox_session, + resource_id: SandboxRequests.identifier(session_id), + subject_id: SandboxRequests.identifier(auth_user_id)) + @requests.call(request, status: 204) + nil + end + end +end diff --git a/maintainers/quality-exceptions.md b/maintainers/quality-exceptions.md index abeb0506..893860a7 100644 --- a/maintainers/quality-exceptions.md +++ b/maintainers/quality-exceptions.md @@ -12,7 +12,7 @@ its missing-method diagnostic when removed from a temporary source copy. | RuboCop `Lint/UnusedPrivateMethod` | The `Client#database_with_token` declaration in `lib/volcano/client.rb` | Realtime's typed `__send__` call uses this private credential-bound factory from another file. The native index cannot see cross-file symbol references; changing visibility would expand the API. | Human-approved on 2026-09-24. [Native index limitation](https://docs.rubocop.org/rubocop/1.90/usage/project_index.html); realtime credential-scoping tests and both strict Steep targets remain active. | | RuboCop `Lint/NumberConversion` | `error.code.to_i` in `GeneratedTransport#error_status`, `lib/volcano/generated_transport.rb` | Generated errors accept Integer, String, or nil. Existing conversion maps absent/malformed statuses to zero and preserves numeric prefixes; `Integer()` raises instead. One internal helper owns this compatibility boundary. | Human-approved on 2026-09-24. `spec/volcano/generated_transport_network_failures_spec.rb` exercises both error paths for nil, zero, empty/invalid strings, numeric strings, and prefixes. [Native cop documents the semantic change](https://docs.rubocop.org/rubocop/1.90/cops_lint.html#lintnumberconversion). | | Steep `Ruby::MethodDefinitionMissing` | `Volcano::Error::VolcanoError#status`, `#code`, and `#retry_after` in `lib/volcano/errors.rb` | Steep cannot infer methods created by `attr_reader`; RuboCop `Style/TrivialAccessors` requires these readers to use `attr_reader`. The three-method `@dynamic` annotation leaves their public RBS types and consumer checks active. | [Steep's documented accessor annotation](https://github.com/soutaro/steep#2-write-ruby-code); removing this annotation produces exactly three `Ruby::MethodDefinitionMissing` diagnostics under `bundle exec steep check --jobs 1`. | -| Steep `Ruby::MethodDefinitionMissing` / `Ruby::NoMethod` | `Client#auth`, `#functions`, `#durable`, `#logs`, `#storage`, `#locks`, `#realtime`, and private `Client::SessionToken#value` in `lib/volcano/client.rb` | Steep 1.10.0 does not infer these `attr_reader` and `Data.define` readers from the handwritten source. Exact `@dynamic` names allow all Client methods and their concrete development signatures to remain checked. | The unannotated class produced missing-reader and missing-method diagnostics; `bundle exec steep check --steepfile Steepfile.transport --jobs 1` now checks all Client methods with zero diagnostics. The public Client RBS remains checked in the primary target and packed consumer tests. | +| Steep `Ruby::MethodDefinitionMissing` / `Ruby::NoMethod` | `Client#sandboxes`, `#auth`, `#functions`, `#durable`, `#logs`, `#storage`, `#locks`, `#realtime`, and private `Client::SessionToken#value` in `lib/volcano/client.rb` | Steep 1.10.0 does not infer these `attr_reader` and `Data.define` readers from the handwritten source. Exact `@dynamic` names allow all Client methods and their concrete development signatures to remain checked. | Adding the Sandbox reader without its annotation produces exactly `Ruby::MethodDefinitionMissing` for `Client#sandboxes`; the inventory test verifies that removing annotations restores every named diagnostic; `bundle exec steep check --steepfile Steepfile.transport --jobs 1` now checks all Client methods with zero diagnostics. The public Client RBS remains checked in the primary target and packed consumer tests. | | Steep `RBS::DuplicatedMethodDefinitionError` | `Client#initialize` in `sig/client.rbs` and `sig_client/client_runtime.rbs` | The public signature lists only consumer keywords, while the development signature also models existing private adapter keywords. Steep 1.10.0 combines signatures across targets in one project and rejects both declarations together. The root-level `Steepfile.transport` checks every remaining handwritten runtime file against the development signature; the primary `Steepfile` and packed consumer tests check the unchanged public signature. | Combining both targets in one Steepfile produces `RBS::DuplicatedMethodDefinitionError`; `bundle exec rake quality:types` runs both all-error targets and validates the public signatures. | | Steep `Ruby::MethodDefinitionMissing` | The eight instance methods and two class methods generated by `Data.define` for `Volcano::SignUpResult` in `lib/volcano/sign_up_result.rb` | Steep cannot infer methods generated by `Data.define` when the class is reopened to check its initializer. The exact-method `@dynamic` annotations preserve the public RBS types and consumer checks. | [Steep's documented dynamic-method annotation](https://github.com/soutaro/steep#2-write-ruby-code); removing both annotations produces exactly ten `Ruby::MethodDefinitionMissing` diagnostics under `bundle exec steep check --jobs 1`, while the annotated target has no diagnostics. | | Steep `Ruby::MethodDefinitionMissing` | Generated field readers in the core-record inventory below, plus `members`, `with`, `to_h`, `deconstruct`, `deconstruct_keys`, `self.[]`, and `self.members` on each listed class | Steep 1.10.0 does not infer `Data.define` methods after the class is reopened for checked initialization. Each `@dynamic` names only a Ruby-generated method; concrete public RBS signatures and packed consumer checks remain active. | [Steep's dynamic-method annotation](https://github.com/soutaro/steep#2-write-ruby-code); `bundle exec steep check --group=core_facades --validate=project --jobs 1` checks the initializers and their callers. | diff --git a/maintainers/steep-dynamic-methods.json b/maintainers/steep-dynamic-methods.json index 8e451434..13c1b87c 100644 --- a/maintainers/steep-dynamic-methods.json +++ b/maintainers/steep-dynamic-methods.json @@ -16,6 +16,7 @@ "locks", "logs", "realtime", + "sandboxes", "storage" ] }, diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 5025c12a..b8d8acbb 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -15,6 +15,8 @@ servers: - url: http://localhost:8000 description: Development API server (use VOLCANO_API_URL env var) tags: + - name: Sandboxes + description: Isolated Linux sessions and reusable templates. - name: Projects description: Project management operations - name: Logs @@ -51,6 +53,8 @@ tags: description: Project-specific public keys for frontend auth - name: Service Keys description: Project-specific secret keys for admin operations (bypass RLS - backend only!) + - name: Project Access Tokens + description: Project-scoped credentials for calling the Volcano API from CI, scripts, and agents - name: OAuth Configuration description: OAuth provider configuration (Google, GitHub, Microsoft, Apple) - name: OAuth Authentication @@ -80,520 +84,717 @@ tags: and polls for completion. Session completion is handled by volcano.dev via the Management API. paths: - /user/imports/connect: - post: + /sandboxes/presets: + get: tags: - - Project Imports - summary: Start a project import provider connection - description: | - Starts a first-party dashboard user's provider connection flow. The - response sets a short-lived HttpOnly browser-binding cookie for the - public provider callback. - operationId: startImportConnect - security: - - UserToken: [] - - AuthUserAccessToken: [] - parameters: - - name: provider - in: query - required: false - description: Import provider to connect. Defaults to Vercel. - schema: - $ref: '#/components/schemas/ImportProvider' - - name: redirect - in: query - required: false - description: Validated application URL used after the provider callback. - schema: - type: string - format: uri - pattern: ^https://[^/?#]+(?:[/?][^#]*)?$|^http://(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?(?:[/?][^#]*)?$ + - Sandboxes + summary: List available sandbox presets + operationId: listSandboxPresets + security: [] responses: '200': - description: Provider authorization URL - content: - application/json: - schema: - $ref: '#/components/schemas/ImportConnectStartResponse' - '400': - description: Unsupported provider or invalid redirect + description: List available sandbox presets content: application/json: schema: - $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + $ref: '#/components/schemas/SandboxPresetList' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to start the connection + /projects/{id}/sandboxes: + get: + tags: + - Sandboxes + summary: List sandbox templates + operationId: listSandboxes + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + responses: + '200': + description: List sandbox templates content: application/json: schema: - $ref: '#/components/schemas/Error' - '503': - description: Import provider integration is not configured + $ref: '#/components/schemas/SandboxTemplatePage' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - /imports/{provider}/callback: - get: + post: tags: - - Project Imports - summary: Complete a project import provider connection - description: | - Public provider callback protected by signed state and the browser-binding - cookie created by startImportConnect. - operationId: completeImportConnect - security: [] + - Sandboxes + summary: Create a sandbox template from a verified preset + operationId: createSandbox + security: + - UserToken: [] + - ServiceRoleKey: [] parameters: - - name: provider + - name: id in: path required: true - schema: - $ref: '#/components/schemas/ImportProvider' - - name: state - in: query - required: true - description: Signed connect state generated by startImportConnect. - schema: - type: string - - name: code - in: query - required: false - description: Provider authorization code. - schema: - type: string - - name: error - in: query - required: false - description: Provider error category. - schema: - type: string - - name: teamId - in: query - required: false - description: Vercel team selected during installation. - schema: - type: string - - name: configurationId - in: query - required: false - description: Vercel Integration configuration identifier. - schema: - type: string - - name: next - in: query - required: false - description: Provider completion URL validated by the provider adapter. schema: type: string - - name: source - in: query - required: false - description: Vercel installation source indicator. + format: uuid + - name: Idempotency-Key + in: header + required: true schema: type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSandboxTemplateRequest' responses: - '303': - description: Redirect to the provider completion URL, signed application redirect, or Volcano import page - headers: - Location: - description: Validated redirect target - schema: - type: string - '400': - description: Invalid callback request, state, or browser binding - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '429': - description: Too many callback attempts - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Failed to complete the connection + '201': + description: Create a sandbox template from a verified preset content: application/json: schema: - $ref: '#/components/schemas/Error' - '503': - description: Import provider integration is not configured + $ref: '#/components/schemas/SandboxTemplate' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - /user/imports/connections: + /projects/{id}/sandboxes/{sandboxId}: get: tags: - - Project Imports - summary: List project import provider connections - operationId: listImportConnections + - Sandboxes + summary: Get a sandbox template + operationId: getSandbox security: - UserToken: [] - - AuthUserAccessToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: sandboxId + in: path + required: true + schema: + type: string + format: uuid responses: '200': - description: Stored import provider connections - content: - application/json: - schema: - $ref: '#/components/schemas/ImportConnectionsResponse' - '401': - description: Not authenticated + description: Get a sandbox template content: application/json: schema: - $ref: '#/components/schemas/Error' - '500': - description: Failed to list connections + $ref: '#/components/schemas/SandboxTemplate' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - /user/imports/connections/{connectionId}: - delete: + patch: tags: - - Project Imports - summary: Delete a project import provider connection - operationId: deleteImportConnection + - Sandboxes + summary: Rename a sandbox template + operationId: updateSandbox security: - UserToken: [] - - AuthUserAccessToken: [] + - ServiceRoleKey: [] parameters: - - name: connectionId + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: sandboxId in: path required: true - description: Connection ID to delete. schema: type: string format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSandboxTemplateRequest' responses: - '204': - description: Connection deleted - '400': - description: Malformed connection ID - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Not authenticated - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '404': - description: Connection not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: Connection changed while it was being deleted - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Failed to delete the connection + '200': + description: Rename a sandbox template content: application/json: schema: - $ref: '#/components/schemas/Error' - '503': - description: Import provider integration is not configured + $ref: '#/components/schemas/SandboxTemplate' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - /imports/{provider}/sources: - get: + delete: tags: - - Project Imports - summary: List project sources available from a provider connection - description: Lists provider projects without changing provider or Volcano resources. - operationId: listImportSources + - Sandboxes + summary: Retire a template and terminate its sessions + operationId: deleteSandbox security: - UserToken: [] - - AuthUserAccessToken: [] + - ServiceRoleKey: [] parameters: - - name: provider + - name: id in: path required: true schema: - $ref: '#/components/schemas/ImportProvider' - - name: connection_id - in: query + type: string + format: uuid + - name: sandboxId + in: path required: true - description: Owned provider connection used for discovery. schema: type: string format: uuid responses: - '200': - description: Provider sources available to import - content: - application/json: - schema: - $ref: '#/components/schemas/ImportSourcesResponse' - '400': - description: Invalid provider or connection ID - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '202': + description: Retire a template and terminate its sessions + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: The provider connection lacks a required Integration scope + /projects/{id}/sandboxes/{sandboxId}/deployments: + get: + tags: + - Sandboxes + summary: List sandbox deployment history + operationId: listSandboxDeployments + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: sandboxId + in: path + required: true + schema: + type: string + format: uuid + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + responses: + '200': + description: List sandbox deployment history content: application/json: schema: - $ref: '#/components/schemas/Error' - '404': - description: Connection or provider source not found + $ref: '#/components/schemas/SandboxDeploymentPage' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: The provider connection must be reconnected + /projects/{id}/sandbox-sessions: + get: + tags: + - Sandboxes + summary: List project sandbox sessions + operationId: listSandboxSessions + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + responses: + '200': + description: List project sandbox sessions content: application/json: schema: - $ref: '#/components/schemas/Error' - '429': - description: Provider rate limit exceeded + $ref: '#/components/schemas/SandboxSessionPage' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to list provider sources + post: + tags: + - Sandboxes + summary: Start a sandbox session + operationId: createSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: Idempotency-Key + in: header + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSandboxSessionRequest' + responses: + '201': + description: Start a sandbox session content: application/json: schema: - $ref: '#/components/schemas/Error' - '503': - description: Provider unavailable or integration not configured + $ref: '#/components/schemas/SandboxSession' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - /imports/{provider}/preflight: + /projects/{id}/sandbox-executions: post: tags: - - Project Imports - summary: Check whether a provider project is ready to import - description: Produces a deterministic read-only readiness report for a proposed new Volcano project. - operationId: preflightProjectImport + - Sandboxes + summary: Execute once and return after confirmed termination + operationId: executeSandbox security: - UserToken: [] - - AuthUserAccessToken: [] + - ServiceRoleKey: [] parameters: - - name: provider + - name: id in: path required: true schema: - $ref: '#/components/schemas/ImportProvider' + type: string + format: uuid + - name: Idempotency-Key + in: header + required: true + schema: + type: string + format: uuid requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ProjectImportPreflightRequest' + $ref: '#/components/schemas/SandboxExecutionRequest' responses: '200': - description: Import readiness report + description: Execute once and return after confirmed termination content: application/json: schema: - $ref: '#/components/schemas/ProjectImportReport' - '400': - description: Invalid provider, source, project name, or target + $ref: '#/components/schemas/SandboxExecutionResult' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + /sandbox-sessions/{sessionId}: + get: + tags: + - Sandboxes + summary: Get a sandbox session + operationId: getSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Get a sandbox session content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': - description: The provider connection lacks a required Integration scope + $ref: '#/components/schemas/SandboxSession' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Connection or provider source not found + delete: + tags: + - Sandboxes + summary: Request sandbox termination + operationId: terminateSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + '202': + description: Request sandbox termination content: application/json: schema: - $ref: '#/components/schemas/Error' - '409': - description: The provider connection must be reconnected + $ref: '#/components/schemas/SandboxSession' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - description: Provider rate limit exceeded + /sandbox-sessions/{sessionId}/suspend: + post: + tags: + - Sandboxes + summary: Suspend a sandbox session + operationId: suspendSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + '202': + description: Suspend a sandbox session content: application/json: schema: - $ref: '#/components/schemas/Error' - '500': - description: Failed to produce an import readiness report + $ref: '#/components/schemas/SandboxSession' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Provider unavailable or integration not configured + /sandbox-sessions/{sessionId}/resume: + post: + tags: + - Sandboxes + summary: Resume a sandbox session + operationId: resumeSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + '202': + description: Resume a sandbox session + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxSession' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - /imports/{provider}/runs: + /sandbox-sessions/{sessionId}/exec: post: tags: - - Project Imports - summary: Start a Vercel project import - description: Creates a Volcano project from an importable production preflight report. Retrying the same request with the same Idempotency-Key returns the existing run. - operationId: startProjectImport + - Sandboxes + summary: Execute a command within a session + operationId: executeSandboxSession security: - UserToken: [] + - ServiceRoleKey: [] - AuthUserAccessToken: [] parameters: - - name: provider + - name: sessionId in: path required: true schema: - $ref: '#/components/schemas/ImportProvider' + type: string + format: uuid - name: Idempotency-Key in: header required: true schema: type: string - minLength: 1 - maxLength: 255 - pattern: ^[!-~]+$ + format: uuid requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ProjectImportStartRequest' + $ref: '#/components/schemas/SandboxCommandRequest' responses: - '202': - description: Import run accepted - headers: - Location: - required: true - schema: - type: string - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectImportRun' - '400': - description: Invalid request or idempotency key - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '200': + description: Execute a command within a session content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': - description: Provider permission or project admission denied + $ref: '#/components/schemas/SandboxCommandResult' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Connection or provider source not found + /sandbox-sessions/{sessionId}/files/read: + post: + tags: + - Sandboxes + summary: Read a workspace file + operationId: readSandboxSessionFile + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxFileReadRequest' + responses: + '200': + description: Read a workspace file content: application/json: schema: - $ref: '#/components/schemas/Error' - '409': - description: Preflight is stale, idempotency key was reused, or destination conflicts + $ref: '#/components/schemas/SandboxFileResult' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '422': - description: Source is not importable + /sandbox-sessions/{sessionId}/files/write: + post: + tags: + - Sandboxes + summary: Write a workspace file + operationId: writeSandboxSessionFile + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxFileWriteRequest' + responses: + '204': + description: Write a workspace file + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - description: Provider rate limit exceeded + /sandbox-sessions/{sessionId}/grants/{subjectId}: + put: + tags: + - Sandboxes + summary: Authorize an authenticated project user for this session + operationId: grantSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + - name: subjectId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxSubjectGrantRequest' + responses: + '204': + description: Authorize an authenticated project user for this session + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to start the import + delete: + tags: + - Sandboxes + summary: Revoke a project user session grant + operationId: revokeSandboxSession + security: + - UserToken: [] + - ServiceRoleKey: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + - name: subjectId + in: path + required: true + schema: + type: string + format: uuid + responses: + '204': + description: Revoke a project user session grant + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Provider unavailable or integration not configured + /sandbox-sessions/{sessionId}/access: + post: + tags: + - Sandboxes + summary: Issue a short-lived port-scoped access credential + operationId: createSandboxSessionAccess + security: + - UserToken: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxAccessRequest' + responses: + '200': + description: Issue a short-lived port-scoped access credential + content: + application/json: + schema: + $ref: '#/components/schemas/SandboxAccess' + default: + description: Request refused or unavailable. Errors include invalid input (400), unauthenticated (401), forbidden (403), not found (404), conflicting retry (409), capacity exhausted (429), and disabled or unavailable (503). content: application/json: schema: $ref: '#/components/schemas/Error' - /imports/{provider}/runs/{runId}: - get: + /user/imports/connect: + post: tags: - Project Imports - summary: Get a project import run - operationId: getProjectImportRun + summary: Start a project import provider connection + description: | + Starts a first-party dashboard user's provider connection flow. The + response sets a short-lived HttpOnly browser-binding cookie for the + public provider callback. + operationId: startImportConnect security: - UserToken: [] - AuthUserAccessToken: [] parameters: - name: provider - in: path - required: true + in: query + required: false + description: Import provider to connect. Defaults to Vercel. schema: $ref: '#/components/schemas/ImportProvider' - - name: runId - in: path - required: true + - name: redirect + in: query + required: false + description: Validated application URL used after the provider callback. schema: type: string - format: uuid + format: uri + pattern: ^https://[^/?#]+(?:[/?][^#]*)?$|^http://(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?(?:[/?][^#]*)?$ responses: '200': - description: Import run status + description: Provider authorization URL content: application/json: schema: - $ref: '#/components/schemas/ProjectImportRun' + $ref: '#/components/schemas/ImportConnectStartResponse' '400': - description: Invalid import run ID + description: Unsupported provider or invalid redirect content: application/json: schema: @@ -604,95 +805,124 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Import run not found + '500': + description: Failed to start the connection content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to get the import run + '503': + description: Import provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /user/git/connect: - post: + /imports/{provider}/callback: + get: tags: - - Git Connections - summary: Start a git provider connection + - Project Imports + summary: Complete a project import provider connection description: | - Starts a first-party dashboard user's git provider connection flow and - returns the provider authorization URL. The response also sets a - short-lived HttpOnly callback binding cookie tied to the authenticated - user through the signed provider state. - operationId: startGitConnect - security: - - UserToken: [] - - AuthUserAccessToken: [] + Public provider callback protected by signed state and the browser-binding + cookie created by startImportConnect. + operationId: completeImportConnect + security: [] parameters: - name: provider + in: path + required: true + schema: + $ref: '#/components/schemas/ImportProvider' + - name: state + in: query + required: true + description: Signed connect state generated by startImportConnect. + schema: + type: string + - name: code in: query required: false - description: Git provider to connect. Defaults to github. + description: Provider authorization code. schema: type: string - enum: - - github - default: github - - name: redirect + - name: error in: query required: false - description: URL to redirect the browser to after the provider callback completes. + description: Provider error category. + schema: + type: string + - name: teamId + in: query + required: false + description: Vercel team selected during installation. + schema: + type: string + - name: configurationId + in: query + required: false + description: Vercel Integration configuration identifier. + schema: + type: string + - name: next + in: query + required: false + description: Provider completion URL validated by the provider adapter. + schema: + type: string + - name: source + in: query + required: false + description: Vercel installation source indicator. schema: type: string responses: - '200': - description: Provider authorization URL - content: - application/json: + '303': + description: Redirect to the provider completion URL, signed application redirect, or Volcano import page + headers: + Location: + description: Validated redirect target schema: - $ref: '#/components/schemas/GitConnectStartResponse' + type: string '400': - description: Unsupported provider or invalid redirect + description: Invalid callback request, state, or browser binding content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '429': + description: Too many callback attempts content: application/json: schema: $ref: '#/components/schemas/Error' '500': - description: Failed to start the connection + description: Failed to complete the connection content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Git provider integration is not configured + description: Import provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /user/git/connections: + /user/imports/connections: get: tags: - - Git Connections - summary: List git provider connections - operationId: listGitConnections + - Project Imports + summary: List project import provider connections + operationId: listImportConnections security: - UserToken: [] - AuthUserAccessToken: [] responses: '200': - description: Stored git provider connections + description: Stored import provider connections content: application/json: schema: - $ref: '#/components/schemas/GitConnectionsResponse' + $ref: '#/components/schemas/ImportConnectionsResponse' '401': description: Not authenticated content: @@ -705,18 +935,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Git provider integration is not configured - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /user/git/connections/{connectionId}: + /user/imports/connections/{connectionId}: delete: tags: - - Git Connections - summary: Delete a git provider connection - operationId: deleteGitConnection + - Project Imports + summary: Delete a project import provider connection + operationId: deleteImportConnection security: - UserToken: [] - AuthUserAccessToken: [] @@ -724,7 +948,7 @@ paths: - name: connectionId in: path required: true - description: Connection ID to delete + description: Connection ID to delete. schema: type: string format: uuid @@ -749,48 +973,56 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to delete connection + '409': + description: Connection changed while it was being deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Failed to delete the connection content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Git provider integration is not configured + description: Import provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /user/git/connections/{connectionId}/installations: + /imports/{provider}/sources: get: tags: - - Git Connections - summary: List GitHub App installations accessible to a connection - description: | - Live proxy to GitHub: lists the platform GitHub App installations the - connection's stored user token can access. Nothing is persisted by - this call. - operationId: listGitInstallations + - Project Imports + summary: List project sources available from a provider connection + description: Lists provider projects without changing provider or Volcano resources. + operationId: listImportSources security: - UserToken: [] - AuthUserAccessToken: [] parameters: - - name: connectionId + - name: provider in: path required: true - description: Connection ID to browse installations for. + schema: + $ref: '#/components/schemas/ImportProvider' + - name: connection_id + in: query + required: true + description: Owned provider connection used for discovery. schema: type: string format: uuid responses: '200': - description: Installations accessible to the connection + description: Provider sources available to import content: application/json: schema: - $ref: '#/components/schemas/GitInstallationsResponse' + $ref: '#/components/schemas/ImportSourcesResponse' '400': - description: Malformed connection ID + description: Invalid provider or connection ID content: application/json: schema: @@ -801,61 +1033,73 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + '403': + description: The provider connection lacks a required Integration scope + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Connection not found + description: Connection or provider source not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: The provider connection must be reconnected + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: Provider rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/Error' '500': - description: Failed to list installations + description: Failed to list provider sources content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Git provider integration is not configured + description: Provider unavailable or integration not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /user/git/connections/{connectionId}/installations/{installationId}/repositories: - get: + /imports/{provider}/preflight: + post: tags: - - Git Connections - summary: List repos accessible to a connection through an installation - description: | - Live proxy to GitHub: lists the repos the connection's stored user - token can access through installationId. Nothing is persisted by this - call. - operationId: listGitInstallationRepositories + - Project Imports + summary: Check whether a provider project is ready to import + description: Produces a deterministic read-only readiness report for a proposed new Volcano project. + operationId: preflightProjectImport security: - UserToken: [] - AuthUserAccessToken: [] parameters: - - name: connectionId - in: path - required: true - description: Connection ID to browse repositories for. - schema: - type: string - format: uuid - - name: installationId + - name: provider in: path required: true - description: GitHub App installation ID. schema: - type: integer - format: int64 + $ref: '#/components/schemas/ImportProvider' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectImportPreflightRequest' responses: '200': - description: Repositories accessible through the installation + description: Import readiness report content: application/json: schema: - $ref: '#/components/schemas/GitRepositoriesResponse' + $ref: '#/components/schemas/ProjectImportReport' '400': - description: Malformed connection or installation ID + description: Invalid provider, source, project name, or target content: application/json: schema: @@ -866,762 +1110,665 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + '403': + description: The provider connection lacks a required Integration scope + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Connection not found + description: Connection or provider source not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: The provider connection must be reconnected + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: Provider rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/Error' '500': - description: Failed to list repositories + description: Failed to produce an import readiness report content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Git provider integration is not configured + description: Provider unavailable or integration not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /github/callback: - get: + /imports/{provider}/runs: + post: tags: - - Git Connections - summary: Complete a GitHub App connection callback - description: | - Public GitHub App callback. The signed state and callback binding cookie - bind the provider authorization to the browser that started the flow. - operationId: gitConnectCallback - security: [] + - Project Imports + summary: Start a Vercel project import + description: Creates a Volcano project from an importable production preflight report. Retrying the same request with the same Idempotency-Key returns the existing run. + operationId: startProjectImport + security: + - UserToken: [] + - AuthUserAccessToken: [] parameters: - - name: code - in: query - required: false - description: GitHub user authorization code. - schema: - type: string - - name: state - in: query + - name: provider + in: path required: true - description: Signed connect state generated by startGitConnect. schema: - type: string - - name: error - in: query - required: false - description: Provider error returned by GitHub. + $ref: '#/components/schemas/ImportProvider' + - name: Idempotency-Key + in: header + required: true schema: type: string + minLength: 1 + maxLength: 255 + pattern: ^[!-~]+$ + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectImportStartRequest' responses: - '303': - description: Redirect back to the app after a successful or failed connect attempt + '202': + description: Import run accepted headers: Location: - description: Redirect target + required: true schema: type: string + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectImportRun' '400': - description: Invalid callback request or state + description: Invalid request or idempotency key + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Not authenticated + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Provider permission or project admission denied + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Connection or provider source not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: Preflight is stale, idempotency key was reused, or destination conflicts + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '422': + description: Source is not importable content: application/json: schema: $ref: '#/components/schemas/Error' '429': - description: Too many callback attempts from this client + description: Provider rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/Error' '500': - description: Failed to complete the connection + description: Failed to start the import content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Git provider integration is not configured + description: Provider unavailable or integration not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /projects: + /imports/{provider}/runs/{runId}: get: tags: - - Projects - summary: List all projects for authenticated user - description: | - Returns projects that are not deleting or deleted, newest first. - Supports two mutually exclusive pagination modes. Offset mode uses - `page` and `limit`. Cursor mode uses `cursor` or `ending_before` with - `limit`, returns `next_cursor`/`prev_cursor`, and supports a bounded - `offset` past the cursor anchor. Supplying `limit` without `page` - selects cursor mode. `search` applies a case-insensitive project-name - filter in either mode. `include` optionally expands each returned - project with its Git connection and/or aggregate health summary using - `git_connection` and `health`. Sending `page` with `cursor` or `ending_before`, - or sending both cursor directions, returns 400. - operationId: listProjects + - Project Imports + summary: Get a project import run + operationId: getProjectImportRun security: - UserToken: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' - - name: include - in: query - required: false - description: Optional comma-separated project metadata expansions. - style: form - explode: false + - name: provider + in: path + required: true schema: - type: array - uniqueItems: true - items: - type: string - enum: - - git_connection - - health + $ref: '#/components/schemas/ImportProvider' + - name: runId + in: path + required: true + schema: + type: string + format: uuid responses: '200': - description: Successful response + description: Import run status content: application/json: schema: - $ref: '#/components/schemas/PaginatedProjects' + $ref: '#/components/schemas/ProjectImportRun' '400': - description: Invalid or conflicting pagination parameters + description: Invalid import run ID content: application/json: schema: $ref: '#/components/schemas/Error' - post: - tags: - - Projects - summary: Create a new project - description: | - Creates a project for the authenticated user. - Each user can create up to 1,000 projects. Requests over this cap return 403. - operationId: createProject - security: - - UserToken: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateProjectRequest' - responses: - '201': - description: Project created + '401': + description: Not authenticated content: application/json: schema: - $ref: '#/components/schemas/Project' - '400': - description: Bad request + $ref: '#/components/schemas/Error' + '404': + description: Import run not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Project limit exceeded for the user + '500': + description: Failed to get the import run content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}: - get: + /user/git/connect: + post: tags: - - Projects - summary: Get project by ID - operationId: getProject + - Git Connections + summary: Start a git provider connection + description: | + Starts a first-party dashboard user's git provider connection flow and + returns the provider authorization URL. The response also sets a + short-lived HttpOnly callback binding cookie tied to the authenticated + user through the signed provider state. + operationId: startGitConnect security: - UserToken: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' + - name: provider + in: query + required: false + description: Git provider to connect. Defaults to github. + schema: + type: string + enum: + - github + default: github + - name: redirect + in: query + required: false + description: URL to redirect the browser to after the provider callback completes. + schema: + type: string responses: '200': - description: Successful response + description: Provider authorization URL content: application/json: schema: - $ref: '#/components/schemas/Project' - '404': - description: Project not found + $ref: '#/components/schemas/GitConnectStartResponse' + '400': + description: Unsupported provider or invalid redirect content: application/json: schema: $ref: '#/components/schemas/Error' - patch: - tags: - - Projects - summary: Update project metadata and region policy - operationId: updateProject - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateProjectRequest' - responses: - '200': - description: Project updated + '401': + description: Not authenticated content: application/json: schema: - $ref: '#/components/schemas/Project' - '400': - description: | - Bad request (no region selected, an unknown region, or — for a - project holding durable functions — a region that does not offer - durable execution) + $ref: '#/components/schemas/Error' + '500': + description: Failed to start the connection content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Forbidden (for example, selecting subset regions on non-PRO plan) + '503': + description: Git provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found + /user/git/connections: + get: + tags: + - Git Connections + summary: List git provider connections + operationId: listGitConnections + security: + - UserToken: [] + - AuthUserAccessToken: [] + responses: + '200': + description: Stored git provider connections + content: + application/json: + schema: + $ref: '#/components/schemas/GitConnectionsResponse' + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: Conflict (project name already exists or a resource deployment blocks a region change) + '500': + description: Failed to list connections content: application/json: schema: $ref: '#/components/schemas/Error' - delete: - tags: - - Projects - summary: Delete a project - description: | - Starts asynchronous project deletion. The project remains available from - `GET /projects/{id}` with `status: deleting` until cleanup finishes, but is - removed from project lists as soon as deletion starts. After cleanup it - returns 404. - operationId: deleteProject - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - responses: - '202': - description: Project deletion started - '404': - description: Project not found + '503': + description: Git provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/health: - get: + /user/git/connections/{connectionId}: + delete: tags: - - Projects - summary: Get project health - description: | - Returns a fast control-plane health snapshot for the project and its - deployed resources. The endpoint does not run live provider probes. - A successful request returns 200 even when the project status is - `unhealthy`; transport and authorization failures use HTTP errors. - operationId: getProjectHealth + - Git Connections + summary: Delete a git provider connection + operationId: deleteGitConnection security: - UserToken: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' + - name: connectionId + in: path + required: true + description: Connection ID to delete + schema: + type: string + format: uuid responses: - '200': - description: Project health snapshot retrieved + '204': + description: Connection deleted + '400': + description: Malformed connection ID content: application/json: schema: - $ref: '#/components/schemas/ProjectHealthResponse' + $ref: '#/components/schemas/Error' '401': - description: Unauthorized - invalid or missing token + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '404': + description: Connection not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found + '500': + description: Failed to delete connection content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '503': + description: Git provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/metrics/query: - post: + /user/git/connections/{connectionId}/installations: + get: tags: - - Projects - summary: Query project runtime metrics + - Git Connections + summary: List GitHub App installations accessible to a connection description: | - Evaluates a batch of named, curated runtime metric queries over one - trailing time range. Query IDs correlate each request with its result; - raw backend query languages are intentionally not exposed. - operationId: queryProjectMetrics + Live proxy to GitHub: lists the platform GitHub App installations the + connection's stored user token can access. Nothing is persisted by + this call. + operationId: listGitInstallations security: - UserToken: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectMetricsQueryRequest' + - name: connectionId + in: path + required: true + description: Connection ID to browse installations for. + schema: + type: string + format: uuid responses: '200': - description: Project runtime metric queries evaluated + description: Installations accessible to the connection content: application/json: schema: - $ref: '#/components/schemas/ProjectMetricsQueryResponse' + $ref: '#/components/schemas/GitInstallationsResponse' '400': - description: Invalid metric query + description: Malformed connection ID content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Unauthorized - invalid or missing token + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '404': - description: Project not found + '404': + description: Connection not found content: application/json: schema: $ref: '#/components/schemas/Error' '500': - description: Internal server error + description: Failed to list installations content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Runtime metrics backend unavailable + description: Git provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/logo: + /user/git/connections/{connectionId}/installations/{installationId}/repositories: get: tags: - - Projects - summary: Get the project logo image - description: | - Returns the raw logo image stored in the project's storage folder. This - endpoint is unauthenticated so the asset can be rendered directly in an - `` tag; project IDs are unguessable UUIDs and logos are - non-sensitive branding. The `Project.logo_url` field exposes a versioned - path to this endpoint. - operationId: getProjectLogo - security: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - responses: - '200': - description: Logo image - content: - image/png: - schema: - type: string - format: binary - image/jpeg: - schema: - type: string - format: binary - image/gif: - schema: - type: string - format: binary - image/webp: - schema: - type: string - format: binary - image/svg+xml: - schema: - type: string - format: binary - '404': - description: Project or logo not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - post: - tags: - - Projects - summary: Upload or replace the project logo + - Git Connections + summary: List repos accessible to a connection through an installation description: | - Uploads an image as the project's logo, storing it in the project's - storage folder. Accepts PNG, JPEG, GIF, WebP, or SVG up to 2 MB. Replaces any - existing logo. Returns the updated project, whose `logo_url` reflects - the new logo. - operationId: uploadProjectLogo + Live proxy to GitHub: lists the repos the connection's stored user + token can access through installationId. Nothing is persisted by this + call. + operationId: listGitInstallationRepositories security: - UserToken: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - logo - properties: - logo: - type: string - format: binary - description: Logo image (PNG, JPEG, GIF, WebP, or SVG; max 2 MB) + - name: connectionId + in: path + required: true + description: Connection ID to browse repositories for. + schema: + type: string + format: uuid + - name: installationId + in: path + required: true + description: GitHub App installation ID. + schema: + type: integer + format: int64 responses: '200': - description: Logo uploaded + description: Repositories accessible through the installation content: application/json: schema: - $ref: '#/components/schemas/Project' + $ref: '#/components/schemas/GitRepositoriesResponse' '400': - description: Bad request (missing file, unsupported type, or too large) + description: Malformed connection or installation ID content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project not found + description: Connection not found content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: Project is being deleted + '500': + description: Failed to list repositories content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Logo storage is not configured + description: Git provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - delete: + /github/callback: + get: tags: - - Projects - summary: Remove the project logo + - Git Connections + summary: Complete a GitHub App connection callback description: | - Deletes the project logo from the project's storage folder and clears its - record. Returns the updated project with no `logo_url`. - operationId: deleteProjectLogo - security: - - UserToken: [] + Public GitHub App callback. The signed state and callback binding cookie + bind the provider authorization to the browser that started the flow. + operationId: gitConnectCallback + security: [] parameters: - - $ref: '#/components/parameters/ProjectId' + - name: code + in: query + required: false + description: GitHub user authorization code. + schema: + type: string + - name: state + in: query + required: true + description: Signed connect state generated by startGitConnect. + schema: + type: string + - name: error + in: query + required: false + description: Provider error returned by GitHub. + schema: + type: string responses: - '200': - description: Logo removed - content: - application/json: + '303': + description: Redirect back to the app after a successful or failed connect attempt + headers: + Location: + description: Redirect target schema: - $ref: '#/components/schemas/Project' - '403': - description: Access denied + type: string + '400': + description: Invalid callback request or state content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found + '429': + description: Too many callback attempts from this client content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: Project is being deleted + '500': + description: Failed to complete the connection content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Logo storage is not configured + description: Git provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/usage: + /projects: get: tags: - Projects - summary: Get usage metrics for a project + summary: List all projects for authenticated user description: | - Returns project usage totals for the current usage month plus - recent hourly and daily time series for each tracked metric. - operationId: getProjectUsage + Returns projects that are not deleting or deleted, newest first. + Supports two mutually exclusive pagination modes. Offset mode uses + `page` and `limit`. Cursor mode uses `cursor` or `ending_before` with + `limit`, returns `next_cursor`/`prev_cursor`, and supports a bounded + `offset` past the cursor anchor. Supplying `limit` without `page` + selects cursor mode. `search` applies a case-insensitive project-name + filter in either mode. `include` optionally expands each returned + project with its Git connection and/or aggregate health summary using + `git_connection` and `health`. Sending `page` with `cursor` or `ending_before`, + or sending both cursor directions, returns 400. + operationId: listProjects security: - UserToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' + - name: include + in: query + required: false + description: Optional comma-separated project metadata expansions. + style: form + explode: false + schema: + type: array + uniqueItems: true + items: + type: string + enum: + - git_connection + - health responses: '200': - description: Usage metrics retrieved - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectUsageResponse' - '403': - description: Access denied + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/Error' - '404': - description: Project not found + $ref: '#/components/schemas/PaginatedProjects' + '400': + description: Invalid or conflicting pagination parameters content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/shared-variables: - put: + post: tags: - Projects - summary: Replace shared variable names + summary: Create a new project description: | - Atomically replaces the complete shared function-variable list without - changing values. Names must already exist. Validates final affected - function environments before membership or propagation side effects. - An empty list clears membership. Omitted names remain stored as non-shared variables. - operationId: replaceSharedVariables + Creates a project for the authenticated user. + Each user can create up to 1,000 projects. Requests over this cap return 403. + operationId: createProject security: - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' requestBody: required: true content: application/json: schema: - type: object - additionalProperties: false - required: - - shared_variables - not: - required: - - expected_shared_variables - - expected_shared_variables_digest - properties: - shared_variables: - type: array - uniqueItems: true - items: - type: string - maxLength: 256 - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - expected_shared_variables: - type: array - uniqueItems: true - description: When present, replace only if the current complete shared list matches this list. - items: - type: string - maxLength: 256 - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - expected_shared_variables_digest: - type: string - minLength: 64 - maxLength: 64 - pattern: ^[a-f0-9]{64}$ - description: SHA-256 of the sorted unique current shared names joined by a newline. Use instead of expected_shared_variables for a compact conditional replacement. + $ref: '#/components/schemas/CreateProjectRequest' responses: - '204': - description: Shared list replaced and affected function synchronization started. - '400': - description: Invalid names or final function environment. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - '404': - description: Project not found - '409': - description: Shared list changed since it was read. + '201': + description: Project created content: application/json: schema: - $ref: '#/components/schemas/Error' - '413': - description: Request body exceeds 4,194,304 bytes + $ref: '#/components/schemas/Project' + '400': + description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Persistence or synchronization failed - '503': - description: Shared variable membership writes are disabled during rollout + '403': + description: Project limit exceeded for the user content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/config: + /projects/{id}: get: tags: - Projects - summary: Export project configuration - description: | - Exports the project's current user-facing configuration as a - declarative manifest. Returns JSON by default. Request the canonical - volcano-config.yaml rendering with `Accept: application/yaml` or - `?format=yaml`; the YAML is returned verbatim as the raw response body - (`Content-Type: application/yaml`) and is meant to be saved as-is. - Variable values and write-only secrets (SMTP password, OAuth client secrets, TLS material) - are omitted from the export; shared_variables contains names only; the YAML rendering adds a header comment - describing how to set them via CLI environment interpolation. - operationId: getProjectConfig + summary: Get project by ID + operationId: getProject security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: format - in: query - required: false - description: Response format override. Takes precedence over the Accept header. - schema: - type: string - enum: - - json - - yaml responses: '200': - description: | - Current project configuration. JSON by default; when YAML is - requested the body is the canonical volcano-config.yaml document - served verbatim with `Content-Type: application/yaml`. - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectConfig' - application/yaml: - schema: - type: string - format: binary - description: | - Canonical volcano-config.yaml document returned verbatim, - ready to be saved as-is. The response body is limited to - 4,194,304 bytes, matching the configuration apply limit. - '401': - description: Unauthorized - invalid or missing token + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/Project' '404': description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - '413': - description: Canonical YAML export exceeds 4,194,304 bytes - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: + patch: tags: - Projects - summary: Apply project configuration - description: | - Validates and applies a declarative configuration manifest to the - project, reconciling each declared section and returning a per-resource - report. Omitted sections are untouched. Declared collection keys - (`variables`, `buckets[].policies`, `auth.providers.oauth`, - `auth.email.templates`, `functions[].schedulers`) are fully synced: - resources absent from the manifest are deleted. Functions, frontends, - databases, and buckets are never created or deleted; manifest entries - for resources that do not exist are skipped and reported in `skipped`, - and existing resources missing from a declared section are reported in - `missing`. Validation failures (including plan-gate violations) return - 422 and nothing is applied. Set `dry_run=true` to get the projected - report without applying changes. Applies are serialized per project; - a concurrent apply returns 409. - operationId: applyProjectConfig + summary: Update project metadata and region policy + operationId: updateProject security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: dry_run - in: query - required: false - description: Validate and report projected actions without applying changes. - schema: - type: boolean - default: false requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ProjectConfig' + $ref: '#/components/schemas/UpdateProjectRequest' responses: '200': - description: | - Apply report. Individual entries may still carry `action: error` - for apply-phase failures (summary.errors > 0); already-applied - changes are not rolled back. + description: Project updated content: application/json: schema: - $ref: '#/components/schemas/ProjectConfigApplyResult' + $ref: '#/components/schemas/Project' '400': - description: Malformed request body + description: | + Bad request (no region selected, an unknown region, or — for a + project holding durable functions — a region that does not offer + durable execution) content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + '403': + description: Forbidden (for example, selecting subset regions on non-PRO plan) content: application/json: schema: @@ -1633,58 +1780,58 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: Another apply is in progress for this project + description: Conflict (project name already exists or a resource deployment blocks a region change) content: application/json: schema: $ref: '#/components/schemas/Error' - '422': - description: Manifest validation failed; nothing was applied - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectConfigValidationErrorResponse' - '503': - description: Shared variable membership writes are disabled during rollout + delete: + tags: + - Projects + summary: Delete a project + description: | + Starts asynchronous project deletion. The project remains available from + `GET /projects/{id}` with `status: deleting` until cleanup finishes, but is + removed from project lists as soon as deletion starts. After cleanup it + returns 404. + operationId: deleteProject + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + responses: + '202': + description: Project deletion started + '404': + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/source-export: + /projects/{id}/health: get: tags: - Projects - - Git Connections - summary: Report the project's source-of-truth state - operationId: getProjectSourceExport + summary: Get project health description: | - Volcano stores the source of the functions and frontend it runs for a - project. This reports whether that source has been written to the - connected repository, and whether the repository has taken over as the - project's source of truth. - - `mode` is `platform`, `git_exporting`, `git_pending`, or `git`. Export - enters `git_exporting` before reading stored source. GitHub's signed - push event confirms that the initial commit reached the production - branch. That push or a newer production push changes the mode to - `git_pending` when it starts a deployment. `exported_at` records that - transition. - - A successful Git run completes the transition when it matches the - recorded repository, production branch, and root directory and actually - dispatches every recorded resource. Ordinary production-branch pushes - deploy without changing a platform-managed project's source ownership. + Returns a fast control-plane health snapshot for the project and its + deployed resources. The endpoint does not run live provider probes. + A successful request returns 200 even when the project status is + `unhealthy`; transport and authorization failures use HTTP errors. + operationId: getProjectHealth security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' responses: '200': - description: The project's source-of-truth state + description: Project health snapshot retrieved content: application/json: schema: - $ref: '#/components/schemas/ProjectSourceExportState' + $ref: '#/components/schemas/ProjectHealthResponse' '401': description: Unauthorized - invalid or missing token content: @@ -1692,7 +1839,7 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - project not owned by the caller + description: Access denied content: application/json: schema: @@ -1709,42 +1856,19 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - '501': - description: Source export is not available in this deployment mode - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + /projects/{id}/metrics/query: post: tags: - Projects - - Git Connections - summary: Initialize an empty repository with a project's stored source - operationId: exportProjectSource + summary: Query project runtime metrics description: | - Creates the first commit in the connected repository and pushes it - directly to the configured production branch. The push enters the - ordinary Git auto-deploy flow. Direct source writes remain frozen until - that deployment succeeds and the repository becomes the source of truth. - - The caller confirms the production branch shown before export. Starting - export pins that branch: later GitHub default-branch changes do not - repoint the project. If the configured branch changed after the caller - read it, the request fails without exporting so the caller can show and - confirm the new value. - - The response lists what the export could not carry: resources with no - successful deployment to take source from (`skipped`), and things no - export can hand back (`omitted`) — migrations, which Volcano stores no - copy of, and credential-shaped files, which are left for their owner to - add. - - Requires a connected repository with no commits or branches, and runs - once. Volcano never creates the repository. If GitHub did not confirm - the push, retrying creates the same commit and adopts it when it already - reached the repository. + Evaluates a batch of named, curated runtime metric queries over one + trailing time range. Query IDs correlate each request with its result; + raw backend query languages are intentionally not exposed. + operationId: queryProjectMetrics security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' requestBody: @@ -1752,16 +1876,16 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ExportProjectSourceRequest' + $ref: '#/components/schemas/ProjectMetricsQueryRequest' responses: - '201': - description: The initial production-branch commit that was pushed + '200': + description: Project runtime metric queries evaluated content: application/json: schema: - $ref: '#/components/schemas/ProjectSourceExport' + $ref: '#/components/schemas/ProjectMetricsQueryResponse' '400': - description: Malformed request body + description: Invalid metric query content: application/json: schema: @@ -1773,38 +1897,13 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - project not owned by the caller + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project not found, or it has no repository connected - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: | - The source has already been exported, the repository has already - taken over as the source of truth, the confirmed production branch - is stale, a function or frontend deployment is still in progress, - or the project has no stored source to export - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '422': - description: | - The repository refused the branch, or its contents cannot be laid - out as a repository — a stored file that only ever carries - credentials, or a layout Git auto-deploy would not read back - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '429': - description: GitHub rate limited the request + description: Project not found content: application/json: schema: @@ -1815,360 +1914,421 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - '501': - description: Source export is not available in this deployment mode - content: - application/json: - schema: - $ref: '#/components/schemas/Error' '503': - description: GitHub integration is not configured + description: Runtime metrics backend unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - delete: + /projects/{id}/logo: + get: tags: - Projects - - Git Connections - summary: Cancel an incomplete source export - operationId: cancelProjectSourceExport + summary: Get the project logo image description: | - Restores platform source writes while the project is in - `git_exporting` or `git_pending`. If Volcano reserved or deployed the - root commit, export remains consumed and cannot be run again. The - connected repository and any commit already pushed to it are unchanged. - security: - - UserToken: [] + Returns the raw logo image stored in the project's storage folder. This + endpoint is unauthenticated so the asset can be rendered directly in an + `` tag; project IDs are unguessable UUIDs and logos are + non-sensitive branding. The `Project.logo_url` field exposes a versioned + path to this endpoint. + operationId: getProjectLogo + security: [] parameters: - $ref: '#/components/parameters/ProjectId' responses: - '204': - description: The incomplete source transition was canceled - '401': - description: Unauthorized - invalid or missing token + '200': + description: Logo image content: - application/json: + image/png: schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden - project not owned by the caller - content: - application/json: + type: string + format: binary + image/jpeg: schema: - $ref: '#/components/schemas/Error' - '404': - description: Project not found - content: - application/json: + type: string + format: binary + image/gif: schema: - $ref: '#/components/schemas/Error' - '409': - description: No incomplete transition exists, or Git already took ownership - content: - application/json: + type: string + format: binary + image/webp: schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: + type: string + format: binary + image/svg+xml: schema: - $ref: '#/components/schemas/Error' - '501': - description: Source export is not available in this deployment mode + type: string + format: binary + '404': + description: Project or logo not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/git-connection/production-branch: - put: + post: tags: - Projects - - Git Connections - summary: Set the branch a project deploys from + summary: Upload or replace the project logo description: | - Changes only the production branch, leaving the repository binding - alone. PUT /projects/{id}/git-connection can also set it, but that is a - full rebind: it needs connection_id, installation_id and a repository - selector resent, and re-resolves the repository against GitHub for a - field that does not depend on it. - - The branch does not have to exist. It is validated as a Git branch name - and nothing more, so a project can be pointed at a branch that is about - to be pushed — the case a repository created empty depends on. - - Setting the branch here marks it as the project's own choice, so a later - default-branch rename on GitHub no longer moves it. Projects that never - set one keep following the repository's default branch. - operationId: setProjectGitProductionBranch + Uploads an image as the project's logo, storing it in the project's + storage folder. Accepts PNG, JPEG, GIF, WebP, or SVG up to 2 MB. Replaces any + existing logo. Returns the updated project, whose `logo_url` reflects + the new logo. + operationId: uploadProjectLogo security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' requestBody: required: true content: - application/json: + multipart/form-data: schema: - $ref: '#/components/schemas/SetProjectGitProductionBranchRequest' + type: object + required: + - logo + properties: + logo: + type: string + format: binary + description: Logo image (PNG, JPEG, GIF, WebP, or SVG; max 2 MB) responses: '200': - description: The project's repo connection, with the new branch + description: Logo uploaded content: application/json: schema: - $ref: '#/components/schemas/ProjectGitConnection' + $ref: '#/components/schemas/Project' '400': - description: | - Malformed request body, or a production_branch that is not a valid - Git branch name - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + description: Bad request (missing file, unsupported type, or too large) content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: Project not owned by the caller + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: | - Project not found, or it has no repository connected. The branch is - part of the connection, so there is nothing to set it on until one - exists. + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': - description: A Git source transition is pending + description: Project is being deleted content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to set the production branch + '503': + description: Logo storage is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/git-connection: - get: + delete: tags: - Projects - - Git Connections - summary: Get a project's repo connection - operationId: getProjectGitConnection + summary: Remove the project logo + description: | + Deletes the project logo from the project's storage folder and clears its + record. Returns the updated project with no `logo_url`. + operationId: deleteProjectLogo security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' responses: '200': - description: The project's current repo connection + description: Logo removed content: application/json: schema: - $ref: '#/components/schemas/ProjectGitConnection' - '401': - description: Unauthorized - invalid or missing token + $ref: '#/components/schemas/Project' + '403': + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Project not owned by the caller + '404': + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found, or has no repo connection + '409': + description: Project is being deleted content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to get project git connection + '503': + description: Logo storage is not configured content: application/json: schema: $ref: '#/components/schemas/Error' - put: + /projects/{id}/usage: + get: tags: - Projects - - Git Connections - summary: Connect or update a project's repo connection + summary: Get usage metrics for a project description: | - Full replace, following Vercel's model: many projects may point at the - same repo, so this only binds the project — it never creates or - deletes git-provider state. Used for both the initial connect and - later edits (repo change, root directory, production branch). - Resolves the repository_id or repo_full_name selector against the repos - accessible through installation_id via connection_id's stored GitHub - user token, then persists repository metadata only from that validated - GitHub response. - operationId: connectProjectGit + Returns project usage totals for the current usage month plus + recent hourly and daily time series for each tracked metric. + operationId: getProjectUsage security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ConnectProjectGitRequest' responses: '200': - description: The project's repo connection - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectGitConnection' - '400': - description: | - Malformed request body, no repository selector, selectors that - identify different repositories, a production_branch that is not a - valid Git branch name, no production_branch on a repository with no - default branch to follow (name one to connect a repository that has - no commits yet), or a production_branch other than the new - repository's default in a request that also changes repository. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + description: Usage metrics retrieved content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ProjectUsageResponse' '403': - description: | - Project not owned by the caller, or the selected repository is not - accessible through installation_id + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project or connection not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: | - A Git source transition is pending or complete, so the recorded - repository and root cannot be changed + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to connect project git - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '503': - description: Git provider integration is not configured - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - delete: + /projects/{id}/shared-variables: + put: tags: - Projects - - Git Connections - summary: Disconnect a project's repo connection - operationId: disconnectProjectGit + summary: Replace shared variable names + description: | + Atomically replaces the complete shared function-variable list without + changing values. Names must already exist. Validates final affected + function environments before membership or propagation side effects. + An empty list clears membership. Omitted names remain stored as non-shared variables. + operationId: replaceSharedVariables security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: false + required: + - shared_variables + not: + required: + - expected_shared_variables + - expected_shared_variables_digest + properties: + shared_variables: + type: array + uniqueItems: true + items: + type: string + maxLength: 256 + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + expected_shared_variables: + type: array + uniqueItems: true + description: When present, replace only if the current complete shared list matches this list. + items: + type: string + maxLength: 256 + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + expected_shared_variables_digest: + type: string + minLength: 64 + maxLength: 64 + pattern: ^[a-f0-9]{64}$ + description: SHA-256 of the sorted unique current shared names joined by a newline. Use instead of expected_shared_variables for a compact conditional replacement. responses: '204': - description: Connection removed + description: Shared list replaced and affected function synchronization started. + '400': + description: Invalid names or final function environment. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': - description: Unauthorized - invalid or missing token + description: Unauthorized + '404': + description: Project not found + '409': + description: Shared list changed since it was read. content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Project not owned by the caller + '413': + description: Request body exceeds 4,194,304 bytes content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found, or has no repo connection + '500': + description: Persistence or synchronization failed + '503': + description: Shared variable membership writes are disabled during rollout + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/frontend-shared-variables: + put: + tags: + - Projects + summary: Replace frontend shared variable names + description: | + Atomically replaces the complete shared frontend-variable list without + changing values. Names must already exist. Validates final affected + frontend environments before membership or propagation side effects. + An empty list clears membership. Omitted names remain stored outside the frontend shared list. + operationId: replaceFrontendSharedVariables + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: false + required: + - frontend_shared_variables + not: + required: + - expected_frontend_shared_variables + - expected_frontend_shared_variables_digest + properties: + frontend_shared_variables: + type: array + uniqueItems: true + items: + type: string + maxLength: 256 + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + expected_frontend_shared_variables: + type: array + uniqueItems: true + description: When present, replace only if the current complete frontend shared list matches this list. + items: + type: string + maxLength: 256 + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + expected_frontend_shared_variables_digest: + type: string + minLength: 64 + maxLength: 64 + pattern: ^[a-f0-9]{64}$ + description: SHA-256 of the sorted unique current shared names joined by a newline. Use instead of expected_frontend_shared_variables for a compact conditional replacement. + responses: + '204': + description: Frontend shared list replaced and affected frontend synchronization started. + '400': + description: Invalid names or final frontend environment. content: application/json: schema: $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '404': + description: Project not found '409': - description: | - A Git source transition is pending or complete, so the recorded - repository cannot be disconnected + description: Frontend shared list changed since it was read. content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to disconnect project git + '413': + description: Request body exceeds 4,194,304 bytes content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/git-deploy-settings: + '500': + description: Persistence or synchronization failed + /projects/{id}/config: get: tags: - Projects - - Git Connections - summary: Get a project's Git auto-deploy settings - operationId: getProjectGitDeploySettings + summary: Export project configuration + description: | + Exports the project's current user-facing configuration as a + declarative manifest. Returns JSON by default. Request the canonical + volcano-config.yaml rendering with `Accept: application/yaml` or + `?format=yaml`; the YAML is returned verbatim as the raw response body + (`Content-Type: application/yaml`) and is meant to be saved as-is. + Variable values and write-only secrets (SMTP password, OAuth client secrets, TLS material) + are omitted from the export; shared_variables and frontend_shared_variables contain names only; the YAML rendering adds a header comment + describing how to set them via CLI environment interpolation. + operationId: getProjectConfig security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' + - name: format + in: query + required: false + description: Response format override. Takes precedence over the Accept header. + schema: + type: string + enum: + - json + - yaml responses: '200': - description: The project's current Git auto-deploy settings + description: | + Current project configuration. JSON by default; when YAML is + requested the body is the canonical volcano-config.yaml document + served verbatim with `Content-Type: application/yaml`. content: application/json: schema: - $ref: '#/components/schemas/ProjectGitDeploySettings' + $ref: '#/components/schemas/ProjectConfig' + application/yaml: + schema: + type: string + format: binary + description: | + Canonical volcano-config.yaml document returned verbatim, + ready to be saved as-is. The response body is limited to + 4,194,304 bytes, matching the configuration apply limit. '401': description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Project not owned by the caller - content: - application/json: - schema: - $ref: '#/components/schemas/Error' '404': description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to get project git deploy settings + '413': + description: Canonical YAML export exceeds 4,194,304 bytes content: application/json: schema: @@ -2176,40 +2336,52 @@ paths: put: tags: - Projects - - Git Connections - summary: Update a project's Git auto-deploy settings + summary: Apply project configuration description: | - Full replace of the project's Git auto-deploy settings: what a push to - the connected repo's production branch deploys. - - Connecting a repository sets auto_deploy_enabled and deploy_functions - to true for a project that has never called this endpoint, so a push - deploys without any further setup. Once these settings have been saved - here they are the project's own: connecting, rebinding, disconnecting - and reconnecting all leave them untouched, including when they were - saved before any repository was connected. Frontend settings are off - until set here; the frontend need not exist when they are saved, since - frontend_name is resolved at deploy time. - operationId: updateProjectGitDeploySettings + Validates and applies a declarative configuration manifest to the + project, reconciling each declared section and returning a per-resource + report. Omitted sections are untouched. Declared collection keys + (`variables`, `buckets[].policies`, `auth.providers.oauth`, + `auth.email.templates`, `functions[].schedulers`) are fully synced: + resources absent from the manifest are deleted. Functions, frontends, + databases, and buckets are never created or deleted; manifest entries + for resources that do not exist are skipped and reported in `skipped`, + and existing resources missing from a declared section are reported in + `missing`. Validation failures (including plan-gate violations) return + 422 and nothing is applied. Set `dry_run=true` to get the projected + report without applying changes. Applies are serialized per project; + a concurrent apply returns 409. + operationId: applyProjectConfig security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' + - name: dry_run + in: query + required: false + description: Validate and report projected actions without applying changes. + schema: + type: boolean + default: false requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateProjectGitDeploySettingsRequest' + $ref: '#/components/schemas/ProjectConfig' responses: '200': - description: The project's updated Git auto-deploy settings + description: | + Apply report. Individual entries may still carry `action: error` + for apply-phase failures (summary.errors > 0); already-applied + changes are not rolled back. content: application/json: schema: - $ref: '#/components/schemas/ProjectGitDeploySettings' + $ref: '#/components/schemas/ProjectConfigApplyResult' '400': - description: Malformed request body, or frontend_app_root without frontend_name + description: Malformed request body content: application/json: schema: @@ -2220,12 +2392,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Project not owned by the caller - content: - application/json: - schema: - $ref: '#/components/schemas/Error' '404': description: Project not found content: @@ -2233,57 +2399,59 @@ paths: schema: $ref: '#/components/schemas/Error' '409': - description: | - A Git source transition is pending, or this change would remove - deploy coverage after Git has taken over + description: Another apply is in progress for this project content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to update project git deploy settings + '422': + description: Manifest validation failed; nothing was applied + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectConfigValidationErrorResponse' + '503': + description: Shared variable membership writes are disabled during rollout content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/queries: + /projects/{id}/source-export: get: tags: - - Databases - summary: Get database queries + - Projects + - Git Connections + summary: Report the project's source-of-truth state + operationId: getProjectSourceExport description: | - Returns the database's current top queries from pg_stat_statements - ranked by total execution time. + Volcano stores the source of the functions and frontend it runs for a + project. This reports whether that source has been written to the + connected repository, and whether the repository has taken over as the + project's source of truth. - **PRO plan required.** This endpoint is only available to projects owned - by users on the PRO billing plan. - operationId: getProjectDatabaseQueries + `mode` is `platform`, `git_exporting`, `git_pending`, or `git`. Export + enters `git_exporting` before reading stored source. GitHub's signed + push event confirms that the initial commit reached the production + branch. That push or a newer production push changes the mode to + `git_pending` when it starts a deployment. `exported_at` records that + transition. + + A successful Git run completes the transition when it matches the + recorded repository, production branch, and root directory and actually + dispatches every recorded resource. Ordinary production-branch pushes + deploy without changing a platform-managed project's source ownership. security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - - name: limit - in: query - description: Maximum number of queries to return. - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 responses: '200': - description: Database query performance retrieved - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseQueryPerformanceResponse' - '400': - description: Bad request - invalid query parameters + description: The project's source-of-truth state content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ProjectSourceExportState' '401': description: Unauthorized - invalid or missing token content: @@ -2291,13 +2459,13 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Access denied + description: Forbidden - project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project or database not found + description: Project not found content: application/json: schema: @@ -2308,86 +2476,60 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /deployments: - get: + '501': + description: Source export is not available in this deployment mode + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: tags: - Projects - summary: List deployments across a user's projects + - Git Connections + summary: Initialize an empty repository with a project's stored source + operationId: exportProjectSource description: | - Lists Function and Frontend deployment attempts across every project the - user owns, newest first. Pass `project_id` to narrow the feed to a single - project. - - Scope is project **ownership** (`projects.user_id`). `owner_id` names - whose deployments to return, not who started them — the actor is - `initiated_by_user_id`, which this endpoint does not filter on. - - With a user token the scope is always the authenticated user: `owner_id` - may be omitted, or set to that same user, but naming anyone else is - refused with 403. Service callers on the management API must pass it, - since they have no authenticated user. - - The owner is not checked for existence: an id with no projects returns an - empty page rather than `404`. Unlike `/users/{id}/usage`, this endpoint is - polled to detect an event, so a caller needs `404` to keep meaning "this - route is not served here" — which is how a consumer notices it is running - against an older release. A mistyped owner therefore reads as "nothing - deployed"; callers that need to tell those apart should verify the user - through `GET /users/{id}` first. + Creates the first commit in the connected repository and pushes it + directly to the configured production branch. The push enters the + ordinary Git auto-deploy flow. Direct source writes remain frozen until + that deployment succeeds and the repository becomes the source of truth. - Ordering is selectable. The default is the feed order — most recent - attempt first. `completed_at.asc` orders by completion, oldest first, and - considers only attempts that finished; combined with `limit=1` and a - `status` filter it answers "when did this user first succeed" in one - bounded query. + The caller confirms the production branch shown before export. Starting + export pins that branch: later GitHub default-branch changes do not + repoint the project. If the configured branch changed after the caller + read it, the request fails without exporting so the caller can show and + confirm the new value. - Both pagination modes are supported, selected exactly as - `/projects/{id}/deployments` selects them: `cursor`/`ending_before` (or a - `limit` with no `page`) uses keyset pagination; otherwise `page`/`limit` - offset pagination. `page` with a cursor, and `cursor` with - `ending_before`, are rejected. + The response lists what the export could not carry: resources with no + successful deployment to take source from (`skipped`), and things no + export can hand back (`omitted`) — migrations, which Volcano stores no + copy of, and credential-shaped files, which are left for their owner to + add. - A cursor is bound to every filter *and* to `order`, so changing any of - them mid-pagination rejects the cursor rather than silently skipping or - repeating rows. The keyset position is `(created_at, id)` for - `created_at.desc` and `(completed_at, id)` for `completed_at.asc`. - operationId: listDeployments + Requires a connected repository with no commits or branches, and runs + once. Volcano never creates the repository. If GitHub did not confirm + the push, retrying creates the same commit and adopts it when it already + reached the repository. security: - UserToken: [] + - ProjectAccessToken: [] parameters: - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/DeploymentOwnerId' - - name: project_id - in: query - required: false - description: Restrict the feed to a single project owned by the user. - schema: - type: string - format: uuid - - name: created_after - in: query - required: false - description: Restrict results to attempts created at or after this timestamp. - schema: - type: string - format: date-time - - $ref: '#/components/parameters/DeploymentResourceType' - - $ref: '#/components/parameters/DeploymentStatus' - - $ref: '#/components/parameters/DeploymentOperation' - - $ref: '#/components/parameters/DeploymentOrder' + - $ref: '#/components/parameters/ProjectId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ExportProjectSourceRequest' responses: - '200': - description: Successful response + '201': + description: The initial production-branch commit that was pushed content: application/json: schema: - $ref: '#/components/schemas/PaginatedProjectDeployments' + $ref: '#/components/schemas/ProjectSourceExport' '400': - description: Bad request - invalid filter or pagination + description: Malformed request body content: application/json: schema: @@ -2399,147 +2541,99 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - owner_id names a different user + description: Forbidden - project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '404': + description: Project not found, or it has no repository connected content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/deployments: - get: - tags: - - Projects - summary: List deployments in a project - description: | - Lists Function and Frontend deployment attempts across the project, - ordered most-recent first. Each item includes a normalized resource - reference so clients can render both resource types without extra - fetches. - operationId: listProjectDeployments - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' - - name: created_after - in: query - required: false - description: Restrict results to attempts created at or after this timestamp. - schema: - type: string - format: date-time - - name: resource_type - in: query - required: false + '409': description: | - Restrict the feed to a single resource type. Omit to return both - Function and Frontend deployments. - schema: - type: string - enum: - - function - - frontend - responses: - '200': - description: Successful response + The source has already been exported, the repository has already + taken over as the source of truth, the confirmed production branch + is stale, a function or frontend deployment is still in progress, + or the project has no stored source to export content: application/json: schema: - $ref: '#/components/schemas/PaginatedProjectDeployments' - '400': - description: Bad request - invalid identifier + $ref: '#/components/schemas/Error' + '422': + description: | + The repository refused the branch, or its contents cannot be laid + out as a repository — a stored file that only ever carries + credentials, or a layout Git auto-deploy would not read back content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + '429': + description: GitHub rate limited the request content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Forbidden - project ownership required + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '501': + description: Source export is not available in this deployment mode content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/deployments/summary: - get: + '503': + description: GitHub integration is not configured + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: tags: - Projects - summary: Summarize deployments in a project + - Git Connections + summary: Cancel an incomplete source export + operationId: cancelProjectSourceExport description: | - Summarizes deployment attempts for one comparable resource pipeline. - Success rate uses conclusive outcomes only: active and deleted attempts - are successful; failed and degraded attempts are failures; in-progress - and superseded attempts are excluded. Median build duration includes - completed, non-superseded attempts with recorded build work, - including failed builds. - operationId: summarizeProjectDeployments + Restores platform source writes while the project is in + `git_exporting` or `git_pending`. If Volcano reserved or deployed the + root commit, export remains consumed and cannot be run again. The + connected repository and any commit already pushed to it are unchanged. security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: search - in: query - required: false - description: Restrict the summary to resource names containing this value. - schema: - type: string - - name: resource_type - in: query - required: true - description: Restrict the summary to one comparable deployment pipeline. - schema: - type: string - enum: - - function - - frontend - - name: created_after - in: query - required: false - description: Restrict results to attempts created at or after this timestamp. - schema: - type: string - format: date-time responses: - '200': - description: Successful response + '204': + description: The incomplete source transition was canceled + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: - $ref: '#/components/schemas/ProjectDeploymentSummary' - '400': - description: Bad request - invalid identifier or filter + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + '404': + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Forbidden - project ownership required + '409': + description: No incomplete transition exists, or Git already took ownership content: application/json: schema: @@ -2550,36 +2644,55 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/domains: - get: + '501': + description: Source export is not available in this deployment mode + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/git-connection/production-branch: + put: tags: - - Frontends - summary: List all custom domains in a project + - Projects + - Git Connections + summary: Set the branch a project deploys from description: | - Project-scoped custom-domain list. Returns every active custom - domain across every frontend in the project (excludes soft-deleted - rows). Each item inlines the linked frontend's id and name so the - UI does not need a second fetch to render the "Linked to" column. - operationId: listProjectCustomDomains + Changes only the production branch, leaving the repository binding + alone. PUT /projects/{id}/git-connection can also set it, but that is a + full rebind: it needs connection_id, installation_id and a repository + selector resent, and re-resolves the repository against GitHub for a + field that does not depend on it. + + The branch does not have to exist. It is validated as a Git branch name + and nothing more, so a project can be pointed at a branch that is about + to be pushed — the case a repository created empty depends on. + + Setting the branch here marks it as the project's own choice, so a later + default-branch rename on GitHub no longer moves it. Projects that never + set one keep following the repository's default branch. + operationId: setProjectGitProductionBranch security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SetProjectGitProductionBranchRequest' responses: '200': - description: Successful response + description: The project's repo connection, with the new branch content: application/json: schema: - $ref: '#/components/schemas/PaginatedProjectCustomDomains' + $ref: '#/components/schemas/ProjectGitConnection' '400': - description: Bad request - invalid identifier + description: | + Malformed request body, or a production_branch that is not a valid + Git branch name content: application/json: schema: @@ -2591,392 +2704,174 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - project ownership required + description: Project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '404': + description: | + Project not found, or it has no repository connected. The branch is + part of the connection, so there is nothing to set it on until one + exists. content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/functions: - get: - tags: - - Functions - summary: List all functions in a project - description: | - Supports two mutually exclusive pagination modes. Offset mode uses `page` - and `limit` and returns `next` (URL). Cursor mode uses `cursor` and - `limit`, supports `search` (case-insensitive name match), and returns - `next_cursor`/`prev_cursor`. Sending both `page` and `cursor` (or `page` - and `search`) returns 400. - operationId: listFunctions - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' - responses: - '200': - description: Successful response + '409': + description: A Git source transition is pending content: application/json: schema: - $ref: '#/components/schemas/PaginatedFunctions' - '404': - description: Project not found + $ref: '#/components/schemas/Error' + '500': + description: Failed to set the production branch content: application/json: schema: $ref: '#/components/schemas/Error' - post: + /projects/{id}/git-connection: + get: tags: - - Functions - summary: Create or update function code - description: | - Upload a serverless function source bundle. Direct API clients may send the function code - as a ZIP or tar.gz archive via multipart/form-data. The API stores a normalized tar.gz - source archive. - Cloud deploys should include source files and dependency manifests/lockfiles, not installed - dependency directories. Volcano installs Node.js, Python, and Ruby dependencies during the - function compile build. - Source archive size is enforced by the API with `SOURCE_ARCHIVE_SIZE_LIMIT_MB`; the CLI - does not apply its own source archive size limit. After the final container image is - built, the publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` before pushing. - Uploaded source archives cannot contain symlink entries. Safe symlinks created during - the cloud build are materialized before publish. - Volcano builds and deploys the function asynchronously after upload. A deployment that starts - immediately returns a Function resource with `status: provisioning`, then transitions to - `active` or `failed`. If another deployment is running, the response preserves the resource's - current status and exposes the queued deployment through `pending_deployment_id`. - Existing function traffic continues to use the last known-good runtime during an update. A failed - update keeps that runtime available and records the attempted deployment as failed. - Only one deployment runs for a given function. A newer request supersedes any queued request - and starts after the running deployment. Different functions and projects deploy concurrently. - If a function with the same name already exists in the project, this operation updates that - function's runtime, handler, and source bundle and returns `200 OK`. - Each project can contain up to 10,000 functions. Creating a new function over this cap returns 403. - operationId: createFunction + - Projects + - Git Connections + summary: Get a project's repo connection + operationId: getProjectGitConnection security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - name - - code - - runtime - properties: - name: - type: string - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) - maxLength: 63 - pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - example: my-api-function - code: - type: string - format: binary - description: ZIP or tar.gz archive containing function source code plus dependency manifests/lockfiles. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz source archive. - runtime: - type: string - enum: - - nodejs22.x - - nodejs24.x - - python3.10 - - python3.11 - - python3.12 - - python3.13 - - python3.14 - - ruby3.3 - - ruby3.4 - - ruby4.0 - description: | - Runtime environment. Required. - - Node.js: nodejs22.x, nodejs24.x - - Python: python3.10, python3.11, python3.12, python3.13, python3.14 - - Ruby: ruby3.3, ruby3.4, ruby4.0 - example: nodejs24.x - handler: - type: string - description: | - The name of the function to invoke. Defaults to "handler" if not specified. - Your code must export/define a function with this name: - - Node.js: exports.handler (in index.js) - - Python: def handler() (in main.py) - - Ruby: def handler() (in main.rb) - default: handler - example: handler - is_public: - type: boolean - description: | - Whether the function can be reached through public invocation - ingress. Omit it to keep the function's current visibility; a - new function starts private. - invocation_mode: - $ref: '#/components/schemas/FunctionInvocationMode' - http_auth_mode: - $ref: '#/components/schemas/FunctionHTTPAuthMode' - openapi_spec: - type: string - description: JSON-encoded OpenAPI 3.0 or 3.1 metadata for an HTTP-mode function. - variable_scope: - type: string - enum: - - all - - scoped - description: | - Which project variables this function receives. `all` (the default) gives it only project variables marked `shared: true`; `scoped` gives it only the variables it selects. Omitting this leaves an existing function's scope unchanged. - variables: - type: string - description: | - JSON-encoded array of project variable names this function requires, on top of the ones detected in its source. A declared name the project does not define is rejected with 400; a detected name it does not define is ignored. Only used when `variable_scope` is `scoped`. Omitting this leaves an existing function's declared names unchanged. responses: '200': - description: Existing function updated; its deployment was started or queued + description: The project's current repo connection content: application/json: schema: - $ref: '#/components/schemas/Function' - '201': - description: Function created and deployment workflow started + $ref: '#/components/schemas/ProjectGitConnection' + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: - $ref: '#/components/schemas/Function' - '400': - description: Bad request (invalid file, too large, etc.) + $ref: '#/components/schemas/Error' + '403': + description: Project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Function limit exceeded for the project - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: | - Function deletion is queued or running, or the name is already held - by a durable function — a function cannot change kind. + '404': + description: Project not found, or has no repo connection content: application/json: schema: $ref: '#/components/schemas/Error' '500': - description: Internal server error (function deployment failed) - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /projects/{id}/functions/{functionId}: - get: - tags: - - Functions - summary: Get function by ID - operationId: getFunction - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FunctionId' - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/Function' - '404': - description: Function not found + description: Failed to get project git connection content: application/json: schema: $ref: '#/components/schemas/Error' - patch: + put: tags: - - Functions - summary: Update function settings - operationId: updateFunction + - Projects + - Git Connections + summary: Connect or update a project's repo connection + description: | + Full replace, following Vercel's model: many projects may point at the + same repo, so this only binds the project — it never creates or + deletes git-provider state. Used for both the initial connect and + later edits (repo change, root directory, production branch). + Resolves the repository_id or repo_full_name selector against the repos + accessible through installation_id via connection_id's stored GitHub + user token, then persists repository metadata only from that validated + GitHub response. + operationId: connectProjectGit security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FunctionId' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateFunctionRequest' - examples: - makePublic: - summary: Make function public (anon keys can invoke) - value: - is_public: true - makePrivate: - summary: Make function private (default behavior) - value: - is_public: false + $ref: '#/components/schemas/ConnectProjectGitRequest' responses: '200': - description: Function updated + description: The project's repo connection content: application/json: schema: - $ref: '#/components/schemas/Function' + $ref: '#/components/schemas/ProjectGitConnection' '400': - description: Bad request + description: | + Malformed request body, no repository selector, selectors that + identify different repositories, a production_branch that is not a + valid Git branch name, no production_branch on a repository with no + default branch to follow (name one to connect a repository that has + no commits yet), or a production_branch other than the new + repository's default in a request that also changes repository. content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Function not found + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - delete: - tags: - - Functions - summary: Delete a function - description: | - Schedules asynchronous function deletion. If another deployment is running, the function - preserves its current status and exposes the queued deletion through `pending_deployment_id`. - Its status changes to `deleting` when cleanup starts. After cleanup, it returns 404 and no - longer appears in function lists. - operationId: deleteFunction - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FunctionId' - responses: - '202': - description: Function deletion started or queued - '404': - description: Function not found + '403': + description: | + Project not owned by the caller, or the selected repository is not + accessible through installation_id content: application/json: schema: $ref: '#/components/schemas/Error' - /functions/{functionId}/invoke: - post: - tags: - - Functions - summary: Invoke a function - description: | - Invoke a serverless function. - - **With Service Key** (admin/background operations): - - Use for background jobs, webhooks, cron, admin operations - - Function receives payload only (no user context) - - Database queries bypass RLS (admin access) - - **With Auth User Token** (user-facing): - - Use for user-initiated actions - - Function receives payload + `__volcano_auth` context: - ```javascript - { - user_id: "uuid", - email: "user@example.com", - project_id: "uuid", - role: "authenticated" or "anonymous" - } - ``` - - Database queries enforce RLS (user-scoped data) - - **With Anon Key** (public function only): - - Requires anon key permission: `functions.invoke` - - Function must have `is_public: true` - - Function receives payload only (no `__volcano_auth`) - - **Transport and CORS:** - - This operation is the authenticated direct RPC endpoint and always uses the - POST `{payload: ...}` contract, including for functions whose DNS ingress is - configured in HTTP mode. - - The geo-routed DNS ingress is `https://{functionId}.functions./`. - - RPC-mode DNS ingress accepts POST at `/`. HTTP-mode DNS ingress accepts GET, - HEAD, POST, PUT, PATCH, and DELETE at `/` and nested paths. - - Direct and RPC-mode CORS preflight advertises `POST, OPTIONS`. HTTP-mode DNS - preflight advertises `GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS`. - - `http_auth_mode: none` applies only to public HTTP-mode DNS ingress; this - direct operation always requires a Volcano credential. - - **Durable functions are not invocable here.** A durable function's id - answers 404, whatever its visibility, because a synchronous call would - run it with no execution record, no idempotency and no concurrency - accounting. Start one with - `POST /durable-functions/{functionId}/executions`. - operationId: invokeFunction - security: - - AnonKey: [] - - ServiceRoleKey: [] - - AuthUserAccessToken: [] - parameters: - - $ref: '#/components/parameters/FunctionId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/FunctionInvocationRequest' - examples: - serviceCall: - summary: Admin/background operation (service key) - value: - payload: - action: process_batch - items: - - 1 - - 2 - - 3 - userCall: - summary: User-initiated call (auth token) - value: - payload: - action: get_profile - anonCall: - summary: Public function call (anon key) - value: - payload: - action: ping_public_endpoint - responses: - '200': - description: Function response (passthrough from function runtime) - headers: - X-Volcano-Version: - description: Volcano API/runtime version that served this invocation (`` in production, `-` in non-production) + '404': + description: Project or connection not found + content: + application/json: schema: - type: string - X-Volcano-Region: - description: Region the function ran in (for example `us-east-1`) + $ref: '#/components/schemas/Error' + '409': + description: | + A Git source transition is pending or complete, so the recorded + repository and root cannot be changed + content: + application/json: schema: - type: string + $ref: '#/components/schemas/Error' + '500': + description: Failed to connect project git content: application/json: schema: - $ref: '#/components/schemas/FunctionInvocationResponse' - '400': - description: Bad request - invalid payload or function in failed state + $ref: '#/components/schemas/Error' + '503': + description: Git provider integration is not configured content: application/json: schema: $ref: '#/components/schemas/Error' + delete: + tags: + - Projects + - Git Connections + summary: Disconnect a project's repo connection + operationId: disconnectProjectGit + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + responses: + '204': + description: Connection removed '401': description: Unauthorized - invalid or missing token content: @@ -2984,237 +2879,183 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - CORS blocked, missing `functions.invoke`, or private function with anon key + description: Project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Function not found + description: Project not found, or has no repo connection content: application/json: schema: $ref: '#/components/schemas/Error' - '429': + '409': description: | - Rate limit exceeded (per-function or project-wide limit), or the - owning platform user's billing-cycle bandwidth allowance (aggregate ingress + - egress) was exceeded. + A Git source transition is pending or complete, so the recorded + repository cannot be disconnected content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: | - Function still provisioning or rate limiting service unavailable. - A freshly deployed (or updated) function may briefly report - `provisioning` and reject invocations until the background status - reconciler observes its deployment workflow completing and transitions - it to `active`. This is expected for a few seconds after deploy; clients - should retry. + '500': + description: Failed to disconnect project git content: application/json: schema: $ref: '#/components/schemas/Error' - default: - description: Function response (passthrough; status code/body/headers come from the function) - headers: - X-Volcano-Version: - description: Volcano API/runtime version that served this invocation (`` in production, `-` in non-production) - schema: - type: string - X-Volcano-Region: - description: Region the function ran in (for example `us-east-1`) + /projects/{id}/git-deploy-settings: + get: + tags: + - Projects + - Git Connections + summary: Get a project's Git auto-deploy settings + operationId: getProjectGitDeploySettings + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + responses: + '200': + description: The project's current Git auto-deploy settings + content: + application/json: schema: - type: string + $ref: '#/components/schemas/ProjectGitDeploySettings' + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: - $ref: '#/components/schemas/FunctionInvocationResponse' - /durable-functions/{functionId}/executions: - post: + $ref: '#/components/schemas/Error' + '403': + description: Project not owned by the caller + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Failed to get project git deploy settings + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: tags: - - Durable Functions - summary: Start a durable execution from an application + - Projects + - Git Connections + summary: Update a project's Git auto-deploy settings description: | - Starts an execution of a durable function using an application - credential, and returns its handle. - - This is the durable counterpart of `POST /functions/{functionId}/invoke`, - and it is the endpoint an application calls. Like that one, it is not - project-scoped: an anon key, a service key and an auth user token each - carry their own project. The project-scoped collection under - `/projects/{id}/durable-functions/...` remains the owner's management - surface. - - **With a service key or an auth user token:** any durable function in - the project. - - **With an anon key:** requires the `functions.invoke` permission, and - the function must have `is_public: true`. - - Starting is all this endpoint does. Reading a result or stopping an - execution requires the project owner's token, because an anon key is - shared by everyone who loads the page and an execution is addressed by - id alone. - - Send `X-Volcano-Execution-Name` to make the start idempotent: repeating - a start with the same name returns the existing execution instead of - beginning a second one. + Full replace of the project's Git auto-deploy settings: what a push to + the connected repo's production branch deploys. - Each execution counts once against the project's durable execution - allowance, however many times the start is retried under the same - execution name, and the number in flight at once is capped by the plan. - The operations the execution performs are counted against the durable - operations allowance when it finishes. - operationId: startDurableExecutionFromApplication + Connecting a repository sets auto_deploy_enabled and deploy_functions + to true for a project that has never called this endpoint, so a push + deploys without any further setup. Once these settings have been saved + here they are the project's own: connecting, rebinding, disconnecting + and reconnecting all leave them untouched, including when they were + saved before any repository was connected. Frontend settings are off + until set here; the frontend need not exist when they are saved, since + frontend_name is resolved at deploy time. + operationId: updateProjectGitDeploySettings security: - - AnonKey: [] - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: - - $ref: '#/components/parameters/DurableFunctionId' - - name: X-Volcano-Execution-Name - in: header - required: false - description: | - Idempotency key for this execution. Generated when omitted. A repeat - under a name that already names a running execution returns that - execution and is not charged again. - - Letters, digits, `-`, `_` and `.`, up to 255 characters. Anything - else is rejected with `400`. - schema: - type: string - maxLength: 255 - pattern: ^[A-Za-z0-9._-]+$ + - $ref: '#/components/parameters/ProjectId' requestBody: - required: false + required: true content: application/json: schema: - description: Input passed to the function, up to 256 KiB. + $ref: '#/components/schemas/UpdateProjectGitDeploySettingsRequest' responses: - '202': - description: Execution accepted and started + '200': + description: The project's updated Git auto-deploy settings content: application/json: schema: - $ref: '#/components/schemas/DurableExecution' + $ref: '#/components/schemas/ProjectGitDeploySettings' '400': - description: Payload is not valid JSON, or the execution name is invalid + description: Malformed request body, or frontend_app_root without frontend_name content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: | - Missing or invalid credential. Also returned for a platform user - token, which is not an application credential; project owners start - executions through the project-scoped collection. + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: | - The anon key lacks `functions.invoke`, the function is not public, - or the request's origin is refused by the project's CORS policy. + description: Project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: | - Durable function not found. Also returned for a standard function's - id and for a durable function in another project, so the response - cannot be used to tell those apart. + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: | - Function is not deployed yet, or has no deployed region. Also - returned when two starts under the same execution name raced and - both released it, which is retryable as it stands. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '413': - description: Payload is larger than 256 KiB - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '429': - description: | - The project has too many executions in flight for its plan, the - function invocation rate limit was exceeded, the project is over - its bandwidth cap, or the account is out of one of its - billing-cycle durable allowances: executions, operations, or - compute. Operations and compute are counted once an execution - finishes, so a refusal on either never interrupts an execution - already running — it declines the next start. + A Git source transition is pending, or this change would remove + deploy coverage after Git has taken over content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: | - Durable execution is not available in this environment, or the - usage limit service could not be reached to charge the start. The - first is returned by a deployment that has no durable execution - engine, such as a local one, and is not retryable there; the second - is transient. + '500': + description: Failed to update project git deploy settings content: application/json: schema: $ref: '#/components/schemas/Error' - /functions/resolve: + /projects/{id}/databases/{databaseName}/queries: get: tags: - - Functions - summary: Resolve function name for invocation + - Databases + summary: Get database queries description: | - Resolves a DNS-safe function name to its function ID within the caller's project. - - SDKs use this endpoint internally to invoke by function name while routing by function ID. - - **With Service Key**: - - Allowed - - **With Auth User Token**: - - Allowed + Returns the database's current top queries from pg_stat_statements + ranked by total execution time. - **With Anon Key**: - - Requires anon key permission: `functions.invoke` - - Function must have `is_public: true` - operationId: resolveFunctionForInvocation + **PRO plan required.** This endpoint is only available to projects owned + by users on the PRO billing plan. + operationId: getProjectDatabaseQueries security: - - AnonKey: [] - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: - - name: name + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DatabaseName' + - name: limit in: query - required: true + description: Maximum number of queries to return. schema: - type: string - maxLength: 63 - pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) - example: my-function + type: integer + minimum: 1 + maximum: 100 + default: 10 responses: '200': - description: Function resolved successfully + description: Database query performance retrieved content: application/json: schema: - $ref: '#/components/schemas/ResolveFunctionResponse' + $ref: '#/components/schemas/DatabaseQueryPerformanceResponse' '400': - description: Bad request - missing or invalid function name + description: Bad request - invalid query parameters content: application/json: schema: @@ -3226,57 +3067,105 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - CORS blocked or missing `functions.invoke` permission for anon key + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: | - Function not found (or private function with anon key). A durable - function is never resolvable here: it is started through - `POST /durable-functions/{functionId}/executions`, not invoked. + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/logs/activity: - post: - tags: - - Logs - summary: Get project log activity - description: | - Retrieve bucketed log counts for one resource type in the project. Set - `resource.type` to `function`, `frontend`, or `database`. Add - `resource.ids` to filter to one or more resources, and add - `resource.deployments.ids` to count deployment logs instead of runtime - logs for functions and frontends. Deployment logs are not supported for - databases. Database logs are a PRO-plan feature; `resource.type=database` - from a FREE-plan project owner returns 403. The activity window is limited - to the plan's retention window (FREE: 1 day, PRO: 30 days); older start - times are clamped to that window. - operationId: getProjectLogActivity - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/LogActivityRequest' - responses: - '200': - description: Successful response + '500': + description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/LogActivityResponse' - '400': - description: Bad request - invalid query parameter - content: - application/json: + $ref: '#/components/schemas/Error' + /deployments: + get: + tags: + - Projects + summary: List deployments across a user's projects + description: | + Lists Function and Frontend deployment attempts across every project the + user owns, newest first. Pass `project_id` to narrow the feed to a single + project. + + Scope is project **ownership** (`projects.user_id`). `owner_id` names + whose deployments to return, not who started them — the actor is + `initiated_by_user_id`, which this endpoint does not filter on. + + With a user token the scope is always the authenticated user: `owner_id` + may be omitted, or set to that same user, but naming anyone else is + refused with 403. Service callers on the management API must pass it, + since they have no authenticated user. + + The owner is not checked for existence: an id with no projects returns an + empty page rather than `404`. Unlike `/users/{id}/usage`, this endpoint is + polled to detect an event, so a caller needs `404` to keep meaning "this + route is not served here" — which is how a consumer notices it is running + against an older release. A mistyped owner therefore reads as "nothing + deployed"; callers that need to tell those apart should verify the user + through `GET /users/{id}` first. + + Ordering is selectable. The default is the feed order — most recent + attempt first. `completed_at.asc` orders by completion, oldest first, and + considers only attempts that finished; combined with `limit=1` and a + `status` filter it answers "when did this user first succeed" in one + bounded query. + + Both pagination modes are supported, selected exactly as + `/projects/{id}/deployments` selects them: `cursor`/`ending_before` (or a + `limit` with no `page`) uses keyset pagination; otherwise `page`/`limit` + offset pagination. `page` with a cursor, and `cursor` with + `ending_before`, are rejected. + + A cursor is bound to every filter *and* to `order`, so changing any of + them mid-pagination rejects the cursor rather than silently skipping or + repeating rows. The keyset position is `(created_at, id)` for + `created_at.desc` and `(completed_at, id)` for `completed_at.asc`. + operationId: listDeployments + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/DeploymentOwnerId' + - name: project_id + in: query + required: false + description: Restrict the feed to a single project owned by the user. + schema: + type: string + format: uuid + - name: created_after + in: query + required: false + description: Restrict results to attempts created at or after this timestamp. + schema: + type: string + format: date-time + - $ref: '#/components/parameters/DeploymentResourceType' + - $ref: '#/components/parameters/DeploymentStatus' + - $ref: '#/components/parameters/DeploymentOperation' + - $ref: '#/components/parameters/DeploymentOrder' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedProjectDeployments' + '400': + description: Bad request - invalid filter or pagination + content: + application/json: schema: $ref: '#/components/schemas/Error' '401': @@ -3286,53 +3175,66 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - project ownership required + description: Forbidden - owner_id names a different user content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project or resource not found + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/logs/search: - post: + /projects/{id}/deployments: + get: tags: - - Logs - summary: Search project logs + - Projects + summary: List deployments in a project description: | - Search or filter logs for one resource type in the project. Set - `resource.type` to `function`, `frontend`, or `database`. Add - `resource.ids` to filter to one or more resources, and add - `resource.deployments.ids` to read deployment logs instead of runtime - logs for functions and frontends. Deployment logs are not supported for - databases. Database logs are a PRO-plan feature; requests for - `resource.type=database` from a FREE-plan project owner return 403. - Log history (runtime and deployment) is limited to the plan's retention - window (FREE: 1 day, PRO: 30 days); older time ranges are clamped to that - window. - operationId: searchProjectLogs + Lists Function and Frontend deployment attempts across the project, + ordered most-recent first. Each item includes a normalized resource + reference so clients can render both resource types without extra + fetches. + operationId: listProjectDeployments security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/LogSearchRequest' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' + - name: created_after + in: query + required: false + description: Restrict results to attempts created at or after this timestamp. + schema: + type: string + format: date-time + - name: resource_type + in: query + required: false + description: | + Restrict the feed to a single resource type. Omit to return both + Function and Frontend deployments. + schema: + type: string + enum: + - function + - frontend responses: '200': description: Successful response content: application/json: schema: - $ref: '#/components/schemas/LogSearchResponse' + $ref: '#/components/schemas/PaginatedProjectDeployments' '400': - description: Bad request - invalid query parameter + description: Bad request - invalid identifier content: application/json: schema: @@ -3349,78 +3251,61 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project or resource not found + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/logs/stream: - post: + /projects/{id}/deployments/summary: + get: tags: - - Logs - summary: Stream project logs + - Projects + summary: Summarize deployments in a project description: | - Live-tail project logs as Server-Sent Events. The request body uses the - resource selector plus `q`, `start_time`, and `limit`, - including runtime logs and function/frontend deployment logs selected - with `resource.deployments`. Deployment logs are not supported for - databases. Database logs are a PRO-plan feature; `resource.type=database` - from a FREE-plan project owner returns 403. The `q` field uses the same - syntax as search and activity requests. Do not send `cursor` or - `end_time`; use `/logs/search` for range backfills. - Explicit historical `start_time` values are limited to the plan's - retention window (FREE: 1 day, PRO: 30 days). Resume with - `Last-Event-ID` or the `last_event_id` query parameter. The cursor is - bound to the request body: the resource selector and every filter must - match the original request when reconnecting, otherwise the request is - rejected with `400`. - - This is a live tail, not a gap-free backfill. On connect or reconnect the - server delivers at most `limit` of the most recent matching events from - the cursor position and then follows new events; events older than that - window are not replayed. Use `/logs/search` to backfill a time range. - operationId: streamProjectLogs + Summarizes deployment attempts for one comparable resource pipeline. + Success rate uses conclusive outcomes only: active and deleted attempts + are successful; failed and degraded attempts are failures; in-progress + and superseded attempts are excluded. Median build duration includes + completed, non-superseded attempts with recorded build work, + including failed builds. + operationId: summarizeProjectDeployments security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: Last-Event-ID - in: header + - name: search + in: query required: false + description: Restrict the summary to resource names containing this value. schema: type: string - description: Opaque stream cursor from the most recent SSE `id` field. - - name: last_event_id + - name: resource_type + in: query + required: true + description: Restrict the summary to one comparable deployment pipeline. + schema: + type: string + enum: + - function + - frontend + - name: created_after in: query required: false + description: Restrict results to attempts created at or after this timestamp. schema: type: string - description: Opaque stream cursor fallback when setting `Last-Event-ID` is not practical. - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/LogStreamRequest' + format: date-time responses: '200': - description: Server-Sent Events stream. `log` events contain a JSON `LogSearchEvent`; `warning` events contain a JSON object with an `error` field. + description: Successful response content: - text/event-stream: + application/json: schema: - type: string - examples: - log: - summary: Log event - value: | - : connected - - id: STREAM_CURSOR - event: log - data: {"id":"LOG_EVENT_ID","timestamp":"2024-01-01T12:00:00Z","level":"info","message":"User logged in","resource":{"type":"function","id":"550e8400-e29b-41d4-a716-446655440000","name":"login"},"region":"us-east-1"} + $ref: '#/components/schemas/ProjectDeploymentSummary' '400': - description: Bad request - invalid selector, stream cursor, or unsupported stream field + description: Bad request - invalid identifier or filter content: application/json: schema: @@ -3437,114 +3322,80 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project or resource not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' '500': - description: Internal server error - log streaming setup failed + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/functions/batch: - post: + /projects/{id}/domains: + get: tags: - - Functions - summary: Deploy multiple functions in one request + - Frontends + summary: List all custom domains in a project description: | - Upload multiple function source archives in one multipart request. Each archive should contain source files - plus dependency manifests/lockfiles, not installed dependency directories. ZIP and tar.gz uploads are - accepted and normalized to tar.gz before storage. The API enforces `SOURCE_ARCHIVE_SIZE_LIMIT_MB` - for each uploaded and normalized source archive. The server records a shared - deployment batch ID for the resulting function deployments. Each function deployment runs its own - compile/publish workflow concurrently, and each publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` - for the final container image. - One batch request can include up to 100 functions. Submit multiple batch requests for larger projects. - If one function fails before its workflow starts, already-started function deployments are left - running and the failed function is reported in the `failed` array. Failed new functions are deleted; - failed updates are rolled back to their previous metadata/status where possible. - operationId: createFunctionsBatch + Project-scoped custom-domain list. Returns every active custom + domain across every frontend in the project (excludes soft-deleted + rows). Each item inlines the linked frontend's id and name so the + UI does not need a second fetch to render the "Linked to" column. + operationId: listProjectCustomDomains security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - properties: - functions: - type: string - description: | - JSON array of functions with `name`, `runtime`, optional `handler`, and `file_field`. Each `file_field` must name a multipart file field containing that function's ZIP or tar.gz source bundle. - - Each entry may also declare `variable_scope` (`all` or `scoped`) and `variables` (an array of project variable names). Omitting them leaves the function's stored declaration unchanged. Volcano detects direct environment references in the uploaded source code and keeps them separate from the declared names: detected names are not written back to the declaration and do not appear in a config export. A scoped function receives its declared names plus the detected ones the project defines; a detected name the project does not define is ignored, since such a reference is often optional. Detection reads code only, so a name appearing solely in a comment or in an unrelated string is not a reference. Declare a name when the function reads it through a computed key, or when it must not deploy without the variable. The request is rejected with 400 before anything is deployed if a scoped function declares a variable the project does not define, or if the resulting environment exceeds 4096 bytes. - code_0: - type: string - format: binary - description: Function ZIP or tar.gz archive referenced by the first manifest entry's `file_field`; additional code_N file fields may be included. Each archive is subject to SOURCE_ARCHIVE_SIZE_LIMIT_MB. - required: - - functions + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' responses: - '202': - description: Batch deployment accepted - content: - application/json: - schema: - $ref: '#/components/schemas/BatchFunctionDeployResponse' - '207': - description: Batch deployment partially accepted; successful functions started deployment and failed functions were compensated where possible + '200': + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/BatchFunctionDeployResponse' + $ref: '#/components/schemas/PaginatedProjectCustomDomains' '400': - description: Bad request + description: Bad request - invalid identifier content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Function limit exceeded for the project + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: | - A name in the batch is held by a function of the other kind — a - function cannot change kind — or the project's source is managed by - Git, where deploys come from a push to the production branch. + '403': + description: Forbidden - project ownership required content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: | - The batch carries a durable function and durable deploys are paused - platform-wide. The same request succeeds once they are re-enabled. + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/schedulers: + /projects/{id}/functions: get: tags: - Functions - summary: List every function scheduler in a project + summary: List all functions in a project description: | - Project-scoped counterpart to `/projects/{id}/functions/{functionId}/schedulers`. - Returns schedulers across all functions in the project, ordered by - creation time descending, with standard page/limit pagination so - clients don't have to fan out one request per function. - operationId: listProjectSchedulers + Supports two mutually exclusive pagination modes. Offset mode uses `page` + and `limit` and returns `next` (URL). Cursor mode uses `cursor` and + `limit`, supports `search` (case-insensitive name match), and returns + `next_cursor`/`prev_cursor`. Sending both `page` and `cursor` (or `page` + and `search`) returns 400. + operationId: listFunctions security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/Page' @@ -3555,31 +3406,13 @@ paths: - $ref: '#/components/parameters/Search' responses: '200': - description: Project schedulers - content: - application/json: - schema: - $ref: '#/components/schemas/FunctionSchedulerListResponse' - /projects/{id}/functions/{functionId}/schedulers: - get: - tags: - - Functions - summary: List schedulers for a function - operationId: listFunctionSchedulers - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FunctionId' - responses: - '200': - description: Function schedulers + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/FunctionSchedulerListResponse' + $ref: '#/components/schemas/PaginatedFunctions' '404': - description: Function not found + description: Project not found content: application/json: schema: @@ -3587,71 +3420,189 @@ paths: post: tags: - Functions - summary: Create a scheduler for a function - description: Creates regional scheduled invocation jobs. Requested regions must be a subset of the function's deployed regions. - operationId: createFunctionScheduler + summary: Create or update function code + description: | + Upload a serverless function source bundle. Direct API clients may send the function code + as a ZIP or tar.gz archive via multipart/form-data. The API stores a normalized tar.gz + source archive. + Cloud deploys should include source files and dependency manifests/lockfiles, not installed + dependency directories. Volcano installs Node.js, Python, and Ruby dependencies during the + function compile build. + Source archive size is enforced by the API with `SOURCE_ARCHIVE_SIZE_LIMIT_MB`; the CLI + does not apply its own source archive size limit. After the final container image is + built, the publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` before pushing. + Uploaded source archives cannot contain symlink entries. Safe symlinks created during + the cloud build are materialized before publish. + Volcano builds and deploys the function asynchronously after upload. A deployment that starts + immediately returns a Function resource with `status: provisioning`, then transitions to + `active` or `failed`. If another deployment is running, the response preserves the resource's + current status and exposes the queued deployment through `pending_deployment_id`. + Existing function traffic continues to use the last known-good runtime during an update. A failed + update keeps that runtime available and records the attempted deployment as failed. + Only one deployment runs for a given function. A newer request supersedes any queued request + and starts after the running deployment. Different functions and projects deploy concurrently. + If a function with the same name already exists in the project, this operation updates that + function's runtime, handler, and source bundle and returns `200 OK`. + Each project can contain up to 10,000 functions. Creating a new function over this cap returns 403. + operationId: createFunction security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FunctionId' requestBody: required: true content: - application/json: + multipart/form-data: schema: - $ref: '#/components/schemas/CreateFunctionSchedulerRequest' + type: object + required: + - name + - code + - runtime + properties: + name: + type: string + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) + maxLength: 63 + pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + example: my-api-function + code: + type: string + format: binary + description: ZIP or tar.gz archive containing function source code plus dependency manifests/lockfiles. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz source archive. + runtime: + type: string + enum: + - nodejs22.x + - nodejs24.x + - python3.10 + - python3.11 + - python3.12 + - python3.13 + - python3.14 + - ruby3.3 + - ruby3.4 + - ruby4.0 + description: | + Runtime environment. Required. + - Node.js: nodejs22.x, nodejs24.x + - Python: python3.10, python3.11, python3.12, python3.13, python3.14 + - Ruby: ruby3.3, ruby3.4, ruby4.0 + example: nodejs24.x + handler: + type: string + description: | + The name of the function to invoke. Defaults to "handler" if not specified. + Your code must export/define a function with this name: + - Node.js: exports.handler (in index.js) + - Python: def handler() (in main.py) + - Ruby: def handler() (in main.rb) + default: handler + example: handler + is_public: + type: boolean + description: | + Whether the function can be reached through public invocation + ingress. Omit it to keep the function's current visibility; a + new function starts private. + invocation_mode: + $ref: '#/components/schemas/FunctionInvocationMode' + http_auth_mode: + $ref: '#/components/schemas/FunctionHTTPAuthMode' + openapi_spec: + type: string + description: JSON-encoded OpenAPI 3.0 or 3.1 metadata for an HTTP-mode function. + variable_scope: + type: string + enum: + - all + - scoped + x-enum-varnames: + - CreateFunctionMultipartBodyVariableScopeAll + - CreateFunctionMultipartBodyVariableScopeScoped + description: | + Which project variables this function receives. `all` (the default) gives it only project variables marked `shared: true`; `scoped` gives it only the variables it selects. Omitting this leaves an existing function's scope unchanged. + variables: + type: string + description: | + JSON-encoded array of project variable names this function requires, on top of the ones detected in its source. A declared name the project does not define is rejected with 400; a detected name it does not define is ignored. Only used when `variable_scope` is `scoped`. Omitting this leaves an existing function's declared names unchanged. responses: + '200': + description: Existing function updated; its deployment was started or queued + content: + application/json: + schema: + $ref: '#/components/schemas/Function' '201': - description: Scheduler created + description: Function created and deployment workflow started content: application/json: schema: - $ref: '#/components/schemas/FunctionScheduler' + $ref: '#/components/schemas/Function' '400': - description: | - Invalid schedule, geofenced region, a scheduler of this name - already exists on the function, or the function is not active. + description: Bad request (invalid file, too large, etc.) content: application/json: schema: $ref: '#/components/schemas/Error' '403': + description: Function limit exceeded for the project + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': description: | - Schedulers are not available on this plan, or the project already - holds as many as the plan allows. The cap counts standard and - durable function schedulers together. + Function deletion is queued or running, or the name is already held + by a durable function — a function cannot change kind. content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Function not found + '429': + description: | + The owner's billing-cycle build-minutes allowance is spent. The + error names the allowance and carries a link to the usage page. content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/functions/{functionId}/schedulers/{schedulerId}: + '500': + description: Internal server error (function deployment failed) + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: | + Build usage could not be read, so the allowance could not be + checked. The same request succeeds once it can be. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/functions/{functionId}: get: tags: - Functions - summary: Get a function scheduler - operationId: getFunctionScheduler + summary: Get function by ID + operationId: getFunction security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/FunctionId' - - $ref: '#/components/parameters/SchedulerId' responses: '200': - description: Function scheduler + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/FunctionScheduler' + $ref: '#/components/schemas/Function' '404': - description: Function or scheduler not found + description: Function not found content: application/json: schema: @@ -3659,37 +3610,50 @@ paths: patch: tags: - Functions - summary: Update a function scheduler - operationId: updateFunctionScheduler + summary: Update function settings + operationId: updateFunction security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/FunctionId' - - $ref: '#/components/parameters/SchedulerId' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateFunctionSchedulerRequest' + $ref: '#/components/schemas/UpdateFunctionRequest' + examples: + makePublic: + summary: Make function public (anon keys can invoke) + value: + is_public: true + makePrivate: + summary: Make function private (default behavior) + value: + is_public: false responses: '200': - description: Scheduler updated + description: Function updated content: application/json: schema: - $ref: '#/components/schemas/FunctionScheduler' + $ref: '#/components/schemas/Function' '400': - description: | - Invalid schedule, geofenced region, or a scheduler of this name - already exists on the function. + description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Function or scheduler not found + description: Function not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: Function settings conflict with attached Frontend Function routes content: application/json: schema: @@ -3697,1241 +3661,1635 @@ paths: delete: tags: - Functions - summary: Delete a function scheduler - operationId: deleteFunctionScheduler + summary: Delete a function + description: | + Schedules asynchronous function deletion. If another deployment is running, the function + preserves its current status and exposes the queued deletion through `pending_deployment_id`. + Its status changes to `deleting` when cleanup starts. After cleanup, it returns 404 and no + longer appears in function lists. + operationId: deleteFunction security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/FunctionId' - - $ref: '#/components/parameters/SchedulerId' responses: - '204': - description: Scheduler deleted + '202': + description: Function deletion started or queued '404': - description: Function or scheduler not found + description: Function not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/functions/{functionId}/deployments: - get: + /functions/{functionId}/invoke: + post: tags: - Functions - summary: List function deployments - operationId: listFunctionDeployments + summary: Invoke a function + description: | + Invoke a serverless function. + + **With Service Key** (admin/background operations): + - Use for background jobs, webhooks, cron, admin operations + - Function receives payload only (no user context) + - Database queries bypass RLS (admin access) + + **With Auth User Token** (user-facing): + - Use for user-initiated actions + - Function receives payload + `__volcano_auth` context: + ```javascript + { + user_id: "uuid", + email: "user@example.com", + project_id: "uuid", + role: "authenticated" or "anonymous" + } + ``` + - Database queries enforce RLS (user-scoped data) + + **With Anon Key** (public function only): + - Requires anon key permission: `functions.invoke` + - Function must have `is_public: true` + - Function receives payload only (no `__volcano_auth`) + + **Transport and CORS:** + - This operation is the authenticated direct RPC endpoint and always uses the + POST `{payload: ...}` contract, including for functions whose DNS ingress is + configured in HTTP mode. + - The geo-routed DNS ingress is the function's `invoke_url`. It is on a + different domain from this API, so it cannot be derived from the API host. + - RPC-mode DNS ingress accepts POST at `/`. HTTP-mode DNS ingress accepts GET, + HEAD, POST, PUT, PATCH, and DELETE at `/` and nested paths. + - Direct and RPC-mode CORS preflight advertises `POST, OPTIONS`. HTTP-mode DNS + preflight advertises `GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS`. + - `http_auth_mode: none` applies only to public HTTP-mode DNS ingress; this + direct operation always requires a Volcano credential. + + **Durable functions are not invocable here.** A durable function's id + answers 404, whatever its visibility, because a synchronous call would + run it with no execution record, no idempotency and no concurrency + accounting. Start one with + `POST /durable-functions/{functionId}/executions`. + operationId: invokeFunction security: - - UserToken: [] + - AnonKey: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/FunctionId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/FunctionInvocationRequest' + examples: + serviceCall: + summary: Admin/background operation (service key) + value: + payload: + action: process_batch + items: + - 1 + - 2 + - 3 + userCall: + summary: User-initiated call (auth token) + value: + payload: + action: get_profile + anonCall: + summary: Public function call (anon key) + value: + payload: + action: ping_public_endpoint responses: '200': - description: Successful response + description: Function response (passthrough from function runtime) + headers: + X-Volcano-Version: + description: Volcano API/runtime version that served this invocation (`` in production, `-` in non-production) + schema: + type: string + X-Volcano-Region: + description: Region the function ran in (for example `us-east-1`) + schema: + type: string + X-Volcano-Proxy-Ms: + description: Milliseconds Volcano spent preparing the invocation, counted from the request arriving until the function was dispatched. Present only when the function was invoked. Does not include function execution. + schema: + type: integer + minimum: 0 + X-Volcano-Proxy-Handler-Ms: + description: The part of `X-Volcano-Proxy-Ms` spent in the invoke endpoint itself. Subtract it from `X-Volcano-Proxy-Ms` to see what authentication and request validation cost. Present only when the function was invoked. + schema: + type: integer + minimum: 0 + X-Volcano-Compute-Ms: + description: Milliseconds spent running the function, from dispatch until it returned. Present only when the function was invoked. Does not include proxy preparation. + schema: + type: integer + minimum: 0 content: application/json: schema: - $ref: '#/components/schemas/PaginatedFunctionDeployments' - '404': - description: Function not found + $ref: '#/components/schemas/FunctionInvocationResponse' + '400': + description: Bad request - invalid payload or function in failed state content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/durable-functions: - get: - tags: - - Durable Functions - summary: List all durable functions in a project - description: | - Standard functions never appear here, and durable functions never appear - under `/projects/{id}/functions`. The two are separate collections. - operationId: listDurableFunctions - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Search' - responses: - '200': - description: Successful response + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: - $ref: '#/components/schemas/PaginatedDurableFunctions' - '400': - description: Bad request - invalid pagination parameters + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - CORS blocked, missing `functions.invoke`, or private function with anon key content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project not found + description: Function not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: | + Rate limit exceeded (per-function or project-wide limit), or the + owning platform user's billing-cycle bandwidth allowance (aggregate ingress + + egress) was exceeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: | + Function still provisioning or rate limiting service unavailable. + A freshly deployed (or updated) function may briefly report + `provisioning` and reject invocations until the background status + reconciler observes its deployment workflow completing and transitions + it to `active`. This is expected for a few seconds after deploy; clients + should retry. content: application/json: schema: $ref: '#/components/schemas/Error' + default: + description: Function response (passthrough; status code/body/headers come from the function) + headers: + X-Volcano-Version: + description: Volcano API/runtime version that served this invocation (`` in production, `-` in non-production) + schema: + type: string + X-Volcano-Region: + description: Region the function ran in (for example `us-east-1`) + schema: + type: string + X-Volcano-Proxy-Ms: + description: Milliseconds Volcano spent preparing the invocation, counted from the request arriving until the function was dispatched. Present only when the function was invoked. Does not include function execution. + schema: + type: integer + minimum: 0 + X-Volcano-Proxy-Handler-Ms: + description: The part of `X-Volcano-Proxy-Ms` spent in the invoke endpoint itself. Subtract it from `X-Volcano-Proxy-Ms` to see what authentication and request validation cost. Present only when the function was invoked. + schema: + type: integer + minimum: 0 + X-Volcano-Compute-Ms: + description: Milliseconds spent running the function, from dispatch until it returned. Present only when the function was invoked. Does not include proxy preparation. + schema: + type: integer + minimum: 0 + content: + application/json: + schema: + $ref: '#/components/schemas/FunctionInvocationResponse' + /durable-functions/{functionId}/executions: post: tags: - Durable Functions - summary: Create or update a durable function + summary: Start a durable execution from an application description: | - Upload a durable function source bundle. Creates the function on the - first call for a name and redeploys it on every call after that, the - same create-or-update contract `POST /projects/{id}/functions` has. + Starts an execution of a durable function using an application + credential, and returns its handle. - Volcano builds and deploys asynchronously. A deployment that starts - immediately returns `status: provisioning`, then transitions to `active` - or `failed`; a deployment that has to wait for a running one is exposed - through `pending_deployment_id`. Existing executions keep running - against the runtime they started on. + This is the durable counterpart of `POST /functions/{functionId}/invoke`, + and it is the endpoint an application calls. Like that one, it is not + project-scoped: an anon key, a service key and an auth user token each + carry their own project. The project-scoped collection under + `/projects/{id}/durable-functions/...` remains the owner's management + surface. - The `durable` configuration is derived from the project's plan rather - than supplied here, and is fixed once the function exists. A name - already held by a standard function is rejected with 409: a function - cannot change kind. - operationId: createDurableFunction - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - name - - code - - runtime - properties: - name: - type: string - description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) - maxLength: 63 - pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - example: order-pipeline - code: - type: string - format: binary - description: ZIP or tar.gz archive containing function source code plus dependency manifests/lockfiles. - runtime: - type: string - enum: - - nodejs22.x - - nodejs24.x - - python3.13 - - python3.14 - description: | - Runtime environment. Required. Durable execution needs the - durable authoring API, which ships for these runtimes only; - any other runtime is rejected with 400 and the response - names the ones that work. Note that a durable Python - function needs a newer runtime than a standard one defaults - to. `GET /functions/runtimes` reports `durable_capable` per - runtime. - example: nodejs24.x - handler: - type: string - description: The name of the function to invoke. Defaults to "handler" if not specified. - default: handler - example: handler - is_public: - type: boolean - description: | - Whether anon keys with `functions.invoke` may start an - execution. Redeploying is the only way to change it, since - the collection has no update endpoint; omit it to keep the - current visibility, and a new function starts private. + **With a service key or an auth user token:** any durable function in + the project. - The standard collection's synchronous invocation fields — - `invocation_mode`, `http_auth_mode`, `openapi_spec` — - configure a request path no durable route serves, and are - rejected with 400 rather than ignored. - variable_scope: - type: string - enum: - - all - - scoped - description: | - Which project variables this function receives. `all` (the default) gives it every project variable; `scoped` gives it only the variables it selects. Omitting this leaves an existing function's scope unchanged. - variables: - type: string - description: | - JSON-encoded array of project variable names this function requires, on top of the ones detected in its source. A declared name the project does not define is rejected with 400; a detected name it does not define is ignored. Only used when `variable_scope` is `scoped`. Omitting this leaves an existing function's declared names unchanged. + **With an anon key:** requires the `functions.invoke` permission, and + the function must have `is_public: true`. + + Starting is all this endpoint does. Reading a result or stopping an + execution requires the project owner's token, because an anon key is + shared by everyone who loads the page and an execution is addressed by + id alone. + + Send `X-Volcano-Execution-Name` to make the start idempotent: repeating + a start with the same name returns the existing execution instead of + beginning a second one. + + Each execution counts once against the project's durable execution + allowance, however many times the start is retried under the same + execution name, and the number in flight at once is capped by the plan. + The operations the execution performs are counted against the durable + operations allowance when it finishes. + operationId: startDurableExecutionFromApplication + security: + - AnonKey: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DurableFunctionId' + - name: X-Volcano-Execution-Name + in: header + required: false + description: | + Idempotency key for this execution. Generated when omitted. A repeat + under a name that already names a running execution returns that + execution and is not charged again. + + Letters, digits, `-`, `_` and `.`, up to 255 characters. Anything + else is rejected with `400`. + schema: + type: string + maxLength: 255 + pattern: ^[A-Za-z0-9._-]+$ + requestBody: + required: false + content: + application/json: + schema: + description: Input passed to the function, up to 256 KiB. responses: - '200': - description: Existing durable function updated; its deployment was started or queued + '202': + description: Execution accepted and started content: application/json: schema: - $ref: '#/components/schemas/DurableFunction' - '201': - description: Durable function created and deployment workflow started + $ref: '#/components/schemas/DurableExecution' + '400': + description: Payload is not valid JSON, or the execution name is invalid content: application/json: schema: - $ref: '#/components/schemas/DurableFunction' - '400': + $ref: '#/components/schemas/Error' + '401': description: | - Bad request (invalid archive, unsupported runtime, invalid name, or a - project region that does not offer durable execution — a durable - function deploys to every region of its project) + Missing or invalid credential. Also returned for a platform user + token, which is not an application credential; project owners start + executions through the project-scoped collection. content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: Durable function limit exceeded for the project + description: | + The anon key lacks `functions.invoke`, the function is not public, + or the request's origin is refused by the project's CORS policy. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: | + Durable function not found. Also returned for a standard function's + id and for a durable function in another project, so the response + cannot be used to tell those apart. content: application/json: schema: $ref: '#/components/schemas/Error' '409': - description: Name is held by a standard function, or a deletion is queued or running + description: | + Function is not deployed yet, or has no deployed region. Also + returned when two starts under the same execution name raced and + both released it, which is retryable as it stands. content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error (function deployment failed) + '413': + description: Payload is larger than 256 KiB + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: | + The project has too many executions in flight for its plan, the + function invocation rate limit was exceeded, the project is over + its bandwidth cap, or the account is out of one of its + billing-cycle durable allowances: executions, operations, or + compute. Operations and compute are counted once an execution + finishes, so a refusal on either never interrupts an execution + already running — it declines the next start. content: application/json: schema: $ref: '#/components/schemas/Error' '503': description: | - Durable deploys are paused platform-wide. The same request succeeds - once they are re-enabled; executions already running are unaffected. + Durable execution is not available in this environment, or the + plan terms for the start could not be read. The first means the + capability is paused or this deployment cannot serve it, so it is + not one to retry in a loop; the second is transient. content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/durable-functions/{functionId}: + /functions/resolve: get: tags: - - Durable Functions - summary: Get durable function by ID or name - operationId: getDurableFunction + - Functions + summary: Resolve function name for invocation + description: | + Resolves a DNS-safe function name to its function ID and invocation URL within the caller's project. + + SDKs use this endpoint internally to invoke by function name while routing by function ID. + Invoke the returned `invoke_url` as-is. It does not share a domain with the API, so a host + built from the API URL will not reach the function. When the deployment serves no public + invocation domain, as in local development, `invoke_url` is omitted and callers invoke + through `POST /functions/{functionId}/invoke`. + + **With Service Key**: + - Allowed + + **With Auth User Token**: + - Allowed + + **With Anon Key**: + - Requires anon key permission: `functions.invoke` + - Function must have `is_public: true` + operationId: resolveFunctionForInvocation security: - - UserToken: [] + - AnonKey: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' + - name: name + in: query + required: true + schema: + type: string + maxLength: 63 + pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) + example: my-function responses: '200': - description: Successful response + description: Function resolved successfully content: application/json: schema: - $ref: '#/components/schemas/DurableFunction' - '404': - description: Durable function not found + $ref: '#/components/schemas/ResolveFunctionResponse' + '400': + description: Bad request - missing or invalid function name content: application/json: schema: $ref: '#/components/schemas/Error' - delete: - tags: - - Durable Functions - summary: Delete a durable function - description: | - Accepted for asynchronous teardown; the work continues after the - response. The function's executions go with it: history stops being - readable whatever `retention_days` had left, and the executions still - running stop counting against the project's concurrency cap. Stop an - execution first if you need it to end before the function does. - operationId: deleteDurableFunction - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - responses: - '202': - description: Deletion accepted and teardown started - '404': - description: | - Durable function not found. Also returned for an id that names a - durable function in another project, so the response cannot be used - to tell the two apart. + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/durable-functions/{functionId}/deployments: - get: - tags: - - Durable Functions - summary: List durable function deployments - operationId: listDurableFunctionDeployments - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - responses: - '200': - description: Successful response + '403': + description: Forbidden - CORS blocked or missing `functions.invoke` permission for anon key content: application/json: schema: - $ref: '#/components/schemas/PaginatedFunctionDeployments' + $ref: '#/components/schemas/Error' '404': - description: Durable function not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /projects/{id}/durable-functions/{functionId}/schedulers: - get: - tags: - - Durable Functions - summary: List schedulers for a durable function - description: | - The durable collection's counterpart to - `/projects/{id}/functions/{functionId}/schedulers`. A standard - function's id is not accepted here, and a durable function's id is not - accepted there. - operationId: listDurableFunctionSchedulers - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - responses: - '200': - description: Durable function schedulers - content: - application/json: - schema: - $ref: '#/components/schemas/FunctionSchedulerListResponse' - '404': - description: Durable function not found + description: | + Function not found (or private function with anon key). A durable + function is never resolvable here: it is started through + `POST /durable-functions/{functionId}/executions`, not invoked. content: application/json: schema: $ref: '#/components/schemas/Error' + /projects/{id}/logs/activity: post: tags: - - Durable Functions - summary: Create a scheduler for a durable function + - Logs + summary: Get project log activity description: | - Each tick starts an execution rather than invoking the function, under - an execution name derived from the run, so a retried tick resolves to - the execution it already started. Requested regions must be a subset of - the function's deployed regions. - - A tick draws on the same durable allowances and concurrency cap a - manual start does, and a tick that would exceed the cap fails that run. - operationId: createDurableFunctionScheduler + Retrieve bucketed log counts for one resource type in the project. Set + `resource.type` to `function`, `frontend`, or `database`. Add + `resource.ids` to filter to one or more resources, and add + `resource.deployments.ids` to count deployment logs instead of runtime + logs for functions and frontends. Deployment logs are not supported for + databases. Database logs are a PRO-plan feature; `resource.type=database` + from a FREE-plan project owner returns 403. The activity window is limited + to the plan's retention window (FREE: 1 day, PRO: 30 days); older start + times are clamped to that window. + operationId: getProjectLogActivity security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/CreateFunctionSchedulerRequest' + $ref: '#/components/schemas/LogActivityRequest' responses: - '201': - description: Scheduler created + '200': + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/FunctionScheduler' + $ref: '#/components/schemas/LogActivityResponse' '400': - description: | - Invalid schedule, geofenced region, a scheduler of this name - already exists on the function, or the function is not active. + description: Bad request - invalid query parameter content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: | - Schedulers are not available on this plan, or the project already - holds as many as the plan allows. The cap counts standard and - durable function schedulers together. + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Durable function not found + '403': + description: Forbidden - project ownership required content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/durable-functions/{functionId}/schedulers/{schedulerId}: - get: - tags: - - Durable Functions - summary: Get a durable function scheduler - operationId: getDurableFunctionScheduler - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - - $ref: '#/components/parameters/SchedulerId' - responses: - '200': - description: Durable function scheduler - content: - application/json: - schema: - $ref: '#/components/schemas/FunctionScheduler' '404': - description: Durable function or scheduler not found + description: Project or resource not found content: application/json: schema: $ref: '#/components/schemas/Error' - patch: + /projects/{id}/logs/search: + post: tags: - - Durable Functions - summary: Update a durable function scheduler - operationId: updateDurableFunctionScheduler + - Logs + summary: Search project logs + description: | + Search or filter logs for one resource type in the project. Set + `resource.type` to `function`, `frontend`, or `database`. Add + `resource.ids` to filter to one or more resources, and add + `resource.deployments.ids` to read deployment logs instead of runtime + logs for functions and frontends. Deployment logs are not supported for + databases. Database logs are a PRO-plan feature; requests for + `resource.type=database` from a FREE-plan project owner return 403. + Log history (runtime and deployment) is limited to the plan's retention + window (FREE: 1 day, PRO: 30 days); older time ranges are clamped to that + window. + operationId: searchProjectLogs security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - - $ref: '#/components/parameters/SchedulerId' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateFunctionSchedulerRequest' + $ref: '#/components/schemas/LogSearchRequest' responses: '200': - description: Scheduler updated + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/FunctionScheduler' + $ref: '#/components/schemas/LogSearchResponse' '400': - description: | - Invalid schedule, geofenced region, or a scheduler of this name - already exists on the function. + description: Bad request - invalid query parameter content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Durable function or scheduler not found + '401': + description: Unauthorized - invalid or missing token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - project ownership required content: application/json: schema: $ref: '#/components/schemas/Error' - delete: - tags: - - Durable Functions - summary: Delete a durable function scheduler - operationId: deleteDurableFunctionScheduler - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - - $ref: '#/components/parameters/SchedulerId' - responses: - '204': - description: Scheduler deleted '404': - description: Durable function or scheduler not found + description: Project or resource not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/durable-functions/{functionId}/executions: + /projects/{id}/logs/stream: post: tags: - - Durable Functions - summary: Start a durable execution + - Logs + summary: Stream project logs description: | - Starts an execution and returns its handle. Never returns a result: an - execution can outlive any request a client could hold open, so the - result is read back from - `GET /projects/{id}/durable-functions/{functionId}/executions/{executionId}`. - - The request body is the execution's input and must be valid JSON if - present. An empty body starts the execution with no input. - - Send `X-Volcano-Execution-Name` to make the start idempotent: repeating a - start with the same name returns the existing execution instead of - beginning a second one. + Live-tail project logs as Server-Sent Events. The request body uses the + resource selector plus `q`, `start_time`, and `limit`, + including runtime logs and function/frontend deployment logs selected + with `resource.deployments`. Deployment logs are not supported for + databases. Database logs are a PRO-plan feature; `resource.type=database` + from a FREE-plan project owner returns 403. The `q` field uses the same + syntax as search and activity requests. Do not send `cursor` or + `end_time`; use `/logs/search` for range backfills. + Explicit historical `start_time` values are limited to the plan's + retention window (FREE: 1 day, PRO: 30 days). Resume with + `Last-Event-ID` or the `last_event_id` query parameter. The cursor is + bound to the request body: the resource selector and every filter must + match the original request when reconnecting, otherwise the request is + rejected with `400`. - Each execution counts against the project's durable execution - allowance, the operations it performs count against the durable - operations allowance when it finishes, and the number of executions in - flight at once is capped by the plan. - operationId: startDurableExecution + This is a live tail, not a gap-free backfill. On connect or reconnect the + server delivers at most `limit` of the most recent matching events from + the cursor position and then follows new events; events older than that + window are not replayed. Use `/logs/search` to backfill a time range. + operationId: streamProjectLogs security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - - name: X-Volcano-Execution-Name + - name: Last-Event-ID in: header required: false - description: | - Idempotency key for this execution. Generated when omitted. A repeat - under a name that already names a running execution returns that - execution and is not charged again. - - Letters, digits, `-`, `_` and `.`, up to 255 characters. Anything - else is rejected with `400`. schema: type: string - maxLength: 255 - pattern: ^[A-Za-z0-9._-]+$ + description: Opaque stream cursor from the most recent SSE `id` field. + - name: last_event_id + in: query + required: false + schema: + type: string + description: Opaque stream cursor fallback when setting `Last-Event-ID` is not practical. requestBody: - required: false + required: true content: application/json: schema: - description: Input passed to the function, up to 256 KiB. + $ref: '#/components/schemas/LogStreamRequest' responses: - '202': - description: Execution accepted and started + '200': + description: Server-Sent Events stream. `log` events contain a JSON `LogSearchEvent`; `warning` events contain a JSON object with an `error` field. content: - application/json: + text/event-stream: schema: - $ref: '#/components/schemas/DurableExecution' + type: string + examples: + log: + summary: Log event + value: | + : connected + + id: STREAM_CURSOR + event: log + data: {"id":"LOG_EVENT_ID","timestamp":"2024-01-01T12:00:00Z","level":"info","message":"User logged in","resource":{"type":"function","id":"550e8400-e29b-41d4-a716-446655440000","name":"login"},"region":"us-east-1"} '400': - description: Payload is not valid JSON, or the execution name is invalid + description: Bad request - invalid selector, stream cursor, or unsupported stream field content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Durable function not found + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: | - Function is not deployed yet, or has no deployed region. Also - returned when two starts under the same execution name raced and - both released it, which is retryable as it stands. + '403': + description: Forbidden - project ownership required content: application/json: schema: $ref: '#/components/schemas/Error' - '413': - description: Payload exceeds the maximum execution input size + '404': + description: Project or resource not found content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - description: | - Too many executions already in flight for this project, or the - account is out of one of its billing-cycle durable allowances: - executions, operations, or compute. An owner-started execution is - metered exactly like an application-started one. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '503': - description: | - Durable execution is not available in this environment, or the - usage limit service could not be reached to charge the start. The - first is returned by a deployment that has no durable execution - engine, such as a local one, and is not retryable there; the second - is transient. + '500': + description: Internal server error - log streaming setup failed content: application/json: schema: $ref: '#/components/schemas/Error' - get: + /projects/{id}/functions/batch: + post: tags: - - Durable Functions - summary: List a durable function's executions + - Functions + summary: Deploy multiple functions in one request description: | - Returns the platform's last observed status for each execution; listing - does not poll each one. Fetch a single execution for its live state. - operationId: listDurableExecutions + Upload multiple function source archives in one multipart request. Each archive should contain source files + plus dependency manifests/lockfiles, not installed dependency directories. ZIP and tar.gz uploads are + accepted and normalized to tar.gz before storage. The API enforces `SOURCE_ARCHIVE_SIZE_LIMIT_MB` + for each uploaded and normalized source archive. The server records a shared + deployment batch ID for the resulting function deployments. Each function deployment runs its own + compile/publish workflow concurrently, and each publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` + for the final container image. + One batch request can include up to 100 functions. Submit multiple batch requests for larger projects. + If one function fails before its workflow starts, already-started function deployments are left + running and the failed function is reported in the `failed` array. Failed new functions are deleted; + failed updates are rolled back to their previous metadata/status where possible. + operationId: createFunctionsBatch security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - name: status - in: query - required: false - description: Return only executions in this status. - schema: - $ref: '#/components/schemas/DurableExecutionStatus' + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + functions: + type: string + description: | + JSON array of functions with `name`, `runtime`, optional `handler`, and `file_field`. Each `file_field` must name a multipart file field containing that function's ZIP or tar.gz source bundle. + + Each entry may also declare `variable_scope` (`all` or `scoped`) and `variables` (an array of project variable names). Omitting them leaves the function's stored declaration unchanged. Volcano detects direct environment references in the uploaded source code and keeps them separate from the declared names: detected names are not written back to the declaration and do not appear in a config export. A scoped function receives its declared names plus the detected ones the project defines; a detected name the project does not define is ignored, since such a reference is often optional. Detection reads code only, so a name appearing solely in a comment or in an unrelated string is not a reference. Declare a name when the function reads it through a computed key, or when it must not deploy without the variable. The request is rejected with 400 before anything is deployed if a scoped function declares a variable the project does not define, or if the resulting environment exceeds 4096 bytes. + code_0: + type: string + format: binary + description: Function ZIP or tar.gz archive referenced by the first manifest entry's `file_field`; additional code_N file fields may be included. Each archive is subject to SOURCE_ARCHIVE_SIZE_LIMIT_MB. + required: + - functions responses: - '200': - description: Successful response + '202': + description: Batch deployment accepted content: application/json: schema: - $ref: '#/components/schemas/PaginatedDurableExecutions' + $ref: '#/components/schemas/BatchFunctionDeployResponse' + '207': + description: Batch deployment partially accepted; successful functions started deployment and failed functions were compensated where possible + content: + application/json: + schema: + $ref: '#/components/schemas/BatchFunctionDeployResponse' '400': - description: Unsupported status filter + description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Durable function not found + '403': + description: Function limit exceeded for the project content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/durable-functions/{functionId}/executions/{executionId}: - get: - tags: - - Durable Functions - summary: Get a durable execution - description: | - Returns the execution's current state, including its `result` once it has - succeeded. Poll this to wait for an execution to finish. - operationId: getDurableExecution - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - - $ref: '#/components/parameters/DurableExecutionId' - responses: - '200': - description: Successful response + '409': + description: | + A name in the batch is held by a function of the other kind — a + function cannot change kind — or the project's source is managed by + Git, where deploys come from a push to the production branch. content: application/json: schema: - $ref: '#/components/schemas/DurableExecution' - '404': - description: Durable function or execution not found + $ref: '#/components/schemas/Error' + '429': + description: | + The owner's billing-cycle build-minutes allowance is spent. The + error names the allowance and carries a link to the usage page. content: application/json: schema: $ref: '#/components/schemas/Error' '503': description: | - Durable execution is not available in this environment. Returned by - a deployment that has no durable execution engine, such as a local - one; the request is not retryable there. + Build usage could not be read, so the allowance could not be + checked. The same request succeeds once it can be. content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/durable-functions/{functionId}/executions/{executionId}/stop: - post: + /projects/{id}/schedulers: + get: tags: - - Durable Functions - summary: Stop a durable execution + - Functions + summary: List every function scheduler in a project description: | - Cancels a running execution. Its completed steps are not undone. - - The call is accepted rather than awaited: cancellation happens behind - it, so the response reports the execution as it was read back and may - still say `running`. Do not branch on that status — the execution - settles into `stopped` shortly after, and polling - `GET /projects/{id}/durable-functions/{functionId}/executions/{executionId}` - is how you see it get there. - - Stopping an execution that already finished is not an error: the - response carries the state it settled in. - operationId: stopDurableExecution + Project-scoped counterpart to `/projects/{id}/functions/{functionId}/schedulers`. + Returns schedulers across all functions in the project, ordered by + creation time descending, with standard page/limit pagination so + clients don't have to fan out one request per function. + operationId: listProjectSchedulers security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DurableFunctionId' - - $ref: '#/components/parameters/DurableExecutionId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' responses: '200': - description: | - Stop accepted. The body is the execution as it was read back, which - may still report `running`. - content: - application/json: - schema: - $ref: '#/components/schemas/DurableExecution' - '404': - description: Durable function or execution not found + description: Project schedulers content: application/json: schema: - $ref: '#/components/schemas/Error' - '409': - description: Execution has not started yet + $ref: '#/components/schemas/FunctionSchedulerListResponse' + /projects/{id}/functions/{functionId}/schedulers: + get: + tags: + - Functions + summary: List schedulers for a function + operationId: listFunctionSchedulers + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/FunctionId' + responses: + '200': + description: Function schedulers content: application/json: schema: - $ref: '#/components/schemas/Error' - '503': - description: | - Durable execution is not available in this environment. Returned by - a deployment that has no durable execution engine, such as a local - one; the request is not retryable there. + $ref: '#/components/schemas/FunctionSchedulerListResponse' + '404': + description: Function not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/frontends: - get: + post: tags: - - Frontends - summary: List all frontends in a project - description: | - Supports two mutually exclusive pagination modes. Offset mode uses `page` - and `limit` and returns `next` (URL). Cursor mode uses `cursor` and - `limit`, supports `search` (case-insensitive name match), and returns - `next_cursor`. Sending both `page` and `cursor` (or `page` and `search`) - returns 400. - operationId: listFrontends + - Functions + summary: Create a scheduler for a function + description: Creates regional scheduled invocation jobs. Requested regions must be a subset of the function's deployed regions. + operationId: createFunctionScheduler security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' + - $ref: '#/components/parameters/FunctionId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFunctionSchedulerRequest' responses: - '200': - description: Successful response + '201': + description: Scheduler created content: application/json: schema: - $ref: '#/components/schemas/PaginatedFrontends' + $ref: '#/components/schemas/FunctionScheduler' '400': - description: Bad request - invalid project identifier + description: | + Invalid schedule, geofenced region, a scheduler of this name + already exists on the function, or the function is not active. content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + '403': + description: | + Schedulers are not available on this plan, or the project already + holds as many as the plan allows. The cap counts standard and + durable function schedulers together. content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Forbidden - project ownership required + '404': + description: Function not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found + /projects/{id}/functions/{functionId}/schedulers/{schedulerId}: + get: + tags: + - Functions + summary: Get a function scheduler + operationId: getFunctionScheduler + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/FunctionId' + - $ref: '#/components/parameters/SchedulerId' + responses: + '200': + description: Function scheduler content: application/json: schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error + $ref: '#/components/schemas/FunctionScheduler' + '404': + description: Function or scheduler not found content: application/json: schema: $ref: '#/components/schemas/Error' - post: + patch: tags: - - Frontends - summary: Create a new frontend deployment - description: | - Creates and deploys a frontend for the project. - If a frontend with the same name already exists in the project, this operation updates that - frontend using the uploaded archive and starts a new deployment. A deployment that starts - immediately returns `status: provisioning`, then transitions to `active`, `degraded`, or - `failed`. If another deployment is running, the response preserves the frontend's current status - and exposes the queued deployment through `pending_deployment_id`. - Existing frontend traffic continues to use an available runtime while the new deployment builds - and provisions. Each deployment publishes its own static assets before the runtimes switch to its - build, and the live build's assets keep serving until the new deployment is live, so a page loaded - mid-deployment resolves its assets whichever build served it. A failed redeploy puts the runtimes - back on the build they were running, leaves the frontend `active` on the previous deployment, and - records the attempted deployment as failed. `degraded` means the runtime remains available but - edge synchronization requires recovery; Volcano retries the edge step without rebuilding. Only one deployment may run for a - given frontend, while independent frontends and projects can deploy concurrently. - For monorepos, provide `app_root` as a relative path from the uploaded archive root - to the Next.js app that should be built. Omit it for single-app archives. - Supported frontend environments are Next.js 15.x and 16.x with Node.js - 22.x or 24.x. The Node.js runtime is inferred from - `package.json` `engines.node`; if omitted, Volcano uses Node.js 22.x. - The selected Node.js family must also satisfy the installed Next.js package's - `engines.node` constraint. Volcano tests Next 15.5.25 (`^18.18.0 || ^19.8.0 || >=20.0.0`) and Next 16.3.5 (`>=20.9.0`). - Source archive size is enforced by the API with `SOURCE_ARCHIVE_SIZE_LIMIT_MB`; the CLI - does not apply its own source archive size limit. After the final container images are - built, the publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` before pushing. - This operation is limited by plan-based frontend deployment quotas (`FREE_FRONTEND_DEPLOYMENTS`, `PRO_FRONTEND_DEPLOYMENTS`). - Each project can contain up to 10,000 frontends regardless of plan. - operationId: createFrontend + - Functions + summary: Update a function scheduler + operationId: updateFunctionScheduler security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/FunctionId' + - $ref: '#/components/parameters/SchedulerId' requestBody: required: true content: - multipart/form-data: + application/json: schema: - type: object - required: - - name - - archive - properties: - name: - type: string - maxLength: 63 - pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - description: DNS-safe frontend name - framework: - type: string - enum: - - nextjs - default: nextjs - description: Next.js frontend. Supported Next.js majors are 15.x and 16.x. - app_root: - type: string - maxLength: 1024 - description: Optional relative POSIX path from the uploaded archive root to the Next.js app to build, for example `apps/web`. - example: apps/web - archive: - type: string - format: binary - description: ZIP or tar.gz archive of the frontend project directory or monorepo workspace root. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz archive. + $ref: '#/components/schemas/UpdateFunctionSchedulerRequest' responses: '200': - description: Existing frontend updated; its deployment was started or queued + description: Scheduler updated content: application/json: schema: - $ref: '#/components/schemas/Frontend' - '201': - description: Frontend created and deployment workflow started + $ref: '#/components/schemas/FunctionScheduler' + '400': + description: | + Invalid schedule, geofenced region, or a scheduler of this name + already exists on the function. content: application/json: schema: - $ref: '#/components/schemas/Frontend' - '400': - description: Bad request + $ref: '#/components/schemas/Error' + '404': + description: Function or scheduler not found content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + delete: + tags: + - Functions + summary: Delete a function scheduler + operationId: deleteFunctionScheduler + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/FunctionId' + - $ref: '#/components/parameters/SchedulerId' + responses: + '204': + description: Scheduler deleted + '404': + description: Function or scheduler not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Frontend deployment limit exceeded for the current plan or project hard cap + /projects/{id}/functions/{functionId}/deployments: + get: + tags: + - Functions + summary: List function deployments + operationId: listFunctionDeployments + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/FunctionId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + responses: + '200': + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/PaginatedFunctionDeployments' '404': - description: Project not found + description: Function not found content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: Conflict - frontend deletion is queued or running + /projects/{id}/durable-functions: + get: + tags: + - Durable Functions + summary: List all durable functions in a project + description: | + Standard functions never appear here, and durable functions never appear + under `/projects/{id}/functions`. The two are separate collections. + operationId: listDurableFunctions + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error + $ref: '#/components/schemas/PaginatedDurableFunctions' + '400': + description: Bad request - invalid pagination parameters content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Service unavailable - frontend workflow or archive limit configuration missing + '404': + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/frontends/{frontendId}: - get: + post: tags: - - Frontends - summary: Get frontend details - operationId: getFrontend + - Durable Functions + summary: Create or update a durable function + description: | + Upload a durable function source bundle. Creates the function on the + first call for a name and redeploys it on every call after that, the + same create-or-update contract `POST /projects/{id}/functions` has. + + Volcano builds and deploys asynchronously. A deployment that starts + immediately returns `status: provisioning`, then transitions to `active` + or `failed`; a deployment that has to wait for a running one is exposed + through `pending_deployment_id`. Existing executions keep running + against the runtime they started on. + + The `durable` configuration is derived from the project's plan rather + than supplied here, and is fixed once the function exists. A name + already held by a standard function is rejected with 409: a function + cannot change kind. + operationId: createDurableFunction security: - UserToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FrontendId' + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: + - name + - code + - runtime + properties: + name: + type: string + description: DNS-safe function name (lowercase letters, numbers, hyphens; cannot start or end with hyphen) + maxLength: 63 + pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + example: order-pipeline + code: + type: string + format: binary + description: ZIP or tar.gz archive containing function source code plus dependency manifests/lockfiles. + runtime: + type: string + enum: + - nodejs22.x + - nodejs24.x + - python3.13 + - python3.14 + description: | + Runtime environment. Required. Durable execution needs the + durable authoring API, which ships for these runtimes only; + any other runtime is rejected with 400 and the response + names the ones that work. Note that a durable Python + function needs a newer runtime than a standard one defaults + to. `GET /functions/runtimes` reports `durable_capable` per + runtime. + example: nodejs24.x + handler: + type: string + description: The name of the function to invoke. Defaults to "handler" if not specified. + default: handler + example: handler + is_public: + type: boolean + description: | + Whether anon keys with `functions.invoke` may start an + execution. Redeploying is the only way to change it, since + the collection has no update endpoint; omit it to keep the + current visibility, and a new function starts private. + + The standard collection's synchronous invocation fields — + `invocation_mode`, `http_auth_mode`, `openapi_spec` — + configure a request path no durable route serves, and are + rejected with 400 rather than ignored. + variable_scope: + type: string + enum: + - all + - scoped + x-enum-varnames: + - CreateDurableFunctionMultipartBodyVariableScopeAll + - CreateDurableFunctionMultipartBodyVariableScopeScoped + description: | + Which project variables this function receives. `all` (the default) gives it every project variable; `scoped` gives it only the variables it selects. Omitting this leaves an existing function's scope unchanged. + variables: + type: string + description: | + JSON-encoded array of project variable names this function requires, on top of the ones detected in its source. A declared name the project does not define is rejected with 400; a detected name it does not define is ignored. Only used when `variable_scope` is `scoped`. Omitting this leaves an existing function's declared names unchanged. responses: '200': - description: Successful response + description: Existing durable function updated; its deployment was started or queued content: application/json: schema: - $ref: '#/components/schemas/Frontend' - '400': - description: Bad request - invalid identifier + $ref: '#/components/schemas/DurableFunction' + '201': + description: Durable function created and deployment workflow started content: application/json: schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + $ref: '#/components/schemas/DurableFunction' + '400': + description: | + Bad request (invalid archive, unsupported runtime, invalid name, or a + project region that does not offer durable execution — a durable + function deploys to every region of its project) content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - project ownership required + description: Durable function limit exceeded for the project content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Frontend not found + '409': + description: Name is held by a standard function, or a deletion is queued or running content: application/json: schema: $ref: '#/components/schemas/Error' '500': - description: Internal server error + description: Internal server error (function deployment failed) content: application/json: schema: $ref: '#/components/schemas/Error' - delete: + '503': + description: | + Durable deploys are paused platform-wide. The same request succeeds + once they are re-enabled; executions already running are unaffected. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/durable-functions/{functionId}: + get: tags: - - Frontends - summary: Delete a frontend - description: | - Schedules asynchronous frontend deletion. If another deployment is running, the frontend - preserves its current status and exposes the queued deletion through `pending_deployment_id`. - Its status changes to `deleting` when cleanup starts. After cleanup, it returns 404 and no - longer appears in frontend lists. - operationId: deleteFrontend + - Durable Functions + summary: Get durable function by ID or name + operationId: getDurableFunction security: - UserToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FrontendId' + - $ref: '#/components/parameters/DurableFunctionId' responses: - '202': - description: Frontend deletion started or queued - '400': - description: Bad request - invalid identifier - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + '200': + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden - project ownership required + $ref: '#/components/schemas/DurableFunction' + '404': + description: Durable function not found content: application/json: schema: $ref: '#/components/schemas/Error' + delete: + tags: + - Durable Functions + summary: Delete a durable function + description: | + Accepted for asynchronous teardown; the work continues after the + response. The function's executions go with it: executions still in + flight are stopped, and history stops being readable whatever + `retention_days` had left. + + Stopping is asynchronous at the platform, and it does not interrupt a + step already running -- that step runs to its next checkpoint. So a + delete ends an execution rather than halting it mid-step; stop the + execution yourself first if you need to observe it ending. + operationId: deleteDurableFunction + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DurableFunctionId' + responses: + '202': + description: Deletion accepted and teardown started '404': - description: Frontend not found + description: | + Durable function not found. Also returned for an id that names a + durable function in another project, so the response cannot be used + to tell the two apart. content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + /projects/{id}/durable-functions/{functionId}/deployments: + get: + tags: + - Durable Functions + summary: List durable function deployments + operationId: listDurableFunctionDeployments + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DurableFunctionId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + responses: + '200': + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/Error' - '503': - description: Service unavailable - frontend workflow configuration missing + $ref: '#/components/schemas/PaginatedFunctionDeployments' + '404': + description: Durable function not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/frontends/{frontendId}/redeploy: - post: + /projects/{id}/durable-functions/{functionId}/schedulers: + get: tags: - - Frontends - summary: Redeploy frontend using latest uploaded artifact + - Durable Functions + summary: List schedulers for a durable function description: | - Starts a new frontend workflow using the latest stored artifact. A deployment that starts - immediately returns `status: provisioning`, then transitions to `active`, `degraded`, or - `failed`. An overlapping deployment preserves the frontend's current status, is exposed through - `pending_deployment_id`, and supersedes any older queued deployment. The previous runtime and its - published static assets remain available during provisioning, and a failed redeploy restores the - regional runtimes to that build and keeps it serving while the attempted deployment is recorded as - failed. - operationId: redeployFrontend + The durable collection's counterpart to + `/projects/{id}/functions/{functionId}/schedulers`. A standard + function's id is not accepted here, and a durable function's id is not + accepted there. + operationId: listDurableFunctionSchedulers security: - UserToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FrontendId' + - $ref: '#/components/parameters/DurableFunctionId' responses: '200': - description: Frontend redeploy started or queued - content: - application/json: - schema: - $ref: '#/components/schemas/Frontend' - '400': - description: Bad request - invalid identifier - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + description: Durable function schedulers content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden - project ownership required + $ref: '#/components/schemas/FunctionSchedulerListResponse' + '404': + description: Durable function not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Frontend not found + post: + tags: + - Durable Functions + summary: Create a scheduler for a durable function + description: | + Each tick starts an execution rather than invoking the function, under + an execution name derived from the run, so a retried tick resolves to + the execution it already started. Requested regions must be a subset of + the function's deployed regions. + + A tick draws on the same durable allowances and concurrency cap a + manual start does, and a tick that would exceed the cap fails that run. + operationId: createDurableFunctionScheduler + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DurableFunctionId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFunctionSchedulerRequest' + responses: + '201': + description: Scheduler created content: application/json: schema: - $ref: '#/components/schemas/Error' - '409': - description: Conflict - frontend deletion is queued or running + $ref: '#/components/schemas/FunctionScheduler' + '400': + description: | + Invalid schedule, geofenced region, a scheduler of this name + already exists on the function, or the function is not active. content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '403': + description: | + Schedulers are not available on this plan, or the project already + holds as many as the plan allows. The cap counts standard and + durable function schedulers together. content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Service unavailable - frontend workflow configuration missing + '404': + description: Durable function not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/frontends/{frontendId}/domain: + /projects/{id}/durable-functions/{functionId}/schedulers/{schedulerId}: get: tags: - - Frontends - summary: Get frontend custom domain status - operationId: getFrontendCustomDomain + - Durable Functions + summary: Get a durable function scheduler + operationId: getDurableFunctionScheduler security: - UserToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FrontendId' + - $ref: '#/components/parameters/DurableFunctionId' + - $ref: '#/components/parameters/SchedulerId' responses: '200': - description: | - Frontend custom domain status, or null when the frontend has no - custom domain configured (the common empty state). - content: - application/json: - schema: - nullable: true - allOf: - - $ref: '#/components/schemas/FrontendCustomDomainResponse' - '400': - description: Bad request - invalid identifier - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden - project ownership required + description: Durable function scheduler content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/FunctionScheduler' '404': - description: Frontend not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error + description: Durable function or scheduler not found content: application/json: schema: $ref: '#/components/schemas/Error' - post: + patch: tags: - - Frontends - summary: Configure frontend custom domain (PRO) - description: | - Configures one custom domain for a frontend. - The default Volcano-generated frontend URL remains active. - Wildcard Volcano frontend TLS remains valid and isolated from custom-domain certificate changes. - operationId: createFrontendCustomDomain + - Durable Functions + summary: Update a durable function scheduler + operationId: updateDurableFunctionScheduler security: - UserToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FrontendId' + - $ref: '#/components/parameters/DurableFunctionId' + - $ref: '#/components/parameters/SchedulerId' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/CreateFrontendCustomDomainRequest' + $ref: '#/components/schemas/UpdateFunctionSchedulerRequest' responses: '200': - description: Custom domain already configured with same hostname + description: Scheduler updated content: application/json: schema: - $ref: '#/components/schemas/FrontendCustomDomainResponse' - '201': - description: Custom domain provisioning started + $ref: '#/components/schemas/FunctionScheduler' + '400': + description: | + Invalid schedule, geofenced region, or a scheduler of this name + already exists on the function. content: application/json: schema: - $ref: '#/components/schemas/FrontendCustomDomainResponse' - '400': - description: Bad request - invalid domain + $ref: '#/components/schemas/Error' + '404': + description: Durable function or scheduler not found content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + delete: + tags: + - Durable Functions + summary: Delete a durable function scheduler + operationId: deleteDurableFunctionScheduler + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DurableFunctionId' + - $ref: '#/components/parameters/SchedulerId' + responses: + '204': + description: Scheduler deleted + '404': + description: Durable function or scheduler not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Forbidden - custom domains require PRO plan + /projects/{id}/durable-functions/{functionId}/executions: + post: + tags: + - Durable Functions + summary: Start a durable execution + description: | + Starts an execution and returns its handle. Never returns a result: an + execution can outlive any request a client could hold open, so the + result is read back from + `GET /projects/{id}/durable-functions/{functionId}/executions/{executionId}`. + + The request body is the execution's input and must be valid JSON if + present. An empty body starts the execution with no input. + + Send `X-Volcano-Execution-Name` to make the start idempotent: repeating a + start with the same name returns the existing execution instead of + beginning a second one. + + Each execution counts against the project's durable execution + allowance, the operations it performs count against the durable + operations allowance when it finishes, and the number of executions in + flight at once is capped by the plan. + operationId: startDurableExecution + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DurableFunctionId' + - name: X-Volcano-Execution-Name + in: header + required: false + description: | + Idempotency key for this execution. Generated when omitted. A repeat + under a name that already names a running execution returns that + execution and is not charged again. + + Letters, digits, `-`, `_` and `.`, up to 255 characters. Anything + else is rejected with `400`. + schema: + type: string + maxLength: 255 + pattern: ^[A-Za-z0-9._-]+$ + requestBody: + required: false + content: + application/json: + schema: + description: Input passed to the function, up to 256 KiB. + responses: + '202': + description: Execution accepted and started + content: + application/json: + schema: + $ref: '#/components/schemas/DurableExecution' + '400': + description: Payload is not valid JSON, or the execution name is invalid content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Frontend not found + description: Durable function not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': - description: Conflict - custom domain already in use, still detaching, or frontend already has a custom domain + description: | + Function is not deployed yet, or has no deployed region. Also + returned when two starts under the same execution name raced and + both released it, which is retryable as it stands. content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '413': + description: Payload exceeds the maximum execution input size + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: | + Too many executions already in flight for this project, or the + account is out of one of its billing-cycle durable allowances: + executions, operations, or compute. An owner-started execution is + metered exactly like an application-started one. content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Service unavailable - custom domain provisioning is temporarily unavailable + description: | + Durable execution is not available in this environment, or the + plan terms for the start could not be read. The first means the + capability is paused or this deployment cannot serve it, so it is + not one to retry in a loop; the second is transient. content: application/json: schema: $ref: '#/components/schemas/Error' - delete: + get: tags: - - Frontends - summary: Delete frontend custom domain - operationId: deleteFrontendCustomDomain + - Durable Functions + summary: List a durable function's executions + description: | + Returns the platform's last observed status for each execution; listing + does not poll each one. Fetch a single execution for its live state. + operationId: listDurableExecutions security: - UserToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FrontendId' + - $ref: '#/components/parameters/DurableFunctionId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - name: status + in: query + required: false + description: Return only executions in this status. + schema: + $ref: '#/components/schemas/DurableExecutionStatus' responses: - '204': - description: Custom domain detach scheduled + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedDurableExecutions' '400': - description: Bad request - invalid identifier + description: Unsupported status filter content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - invalid or missing token + '404': + description: Durable function not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Forbidden - project ownership required + /projects/{id}/durable-functions/{functionId}/executions/{executionId}: + get: + tags: + - Durable Functions + summary: Get a durable execution + description: | + Returns the execution's current state, including its `result` once it has + succeeded. Poll this to wait for an execution to finish. + operationId: getDurableExecution + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DurableFunctionId' + - $ref: '#/components/parameters/DurableExecutionId' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/DurableExecution' + '404': + description: Durable function or execution not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: | + Durable execution is not available in this environment. Either the + capability is paused or this deployment cannot serve it, so the + request is not one to retry in a loop. content: application/json: schema: $ref: '#/components/schemas/Error' + /projects/{id}/durable-functions/{functionId}/executions/{executionId}/stop: + post: + tags: + - Durable Functions + summary: Stop a durable execution + description: | + Cancels a running execution. Its completed steps are not undone. + + The call is accepted rather than awaited: cancellation happens behind + it, so the response reports the execution as it was read back and may + still say `running`. Do not branch on that status — the execution + settles into `stopped` shortly after, and polling + `GET /projects/{id}/durable-functions/{functionId}/executions/{executionId}` + is how you see it get there. + + Stopping an execution that already finished is not an error: the + response carries the state it settled in. + operationId: stopDurableExecution + security: + - UserToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DurableFunctionId' + - $ref: '#/components/parameters/DurableExecutionId' + responses: + '200': + description: | + Stop accepted. The body is the execution as it was read back, which + may still report `running`. + content: + application/json: + schema: + $ref: '#/components/schemas/DurableExecution' '404': - description: Frontend or custom domain not found + description: Durable function or execution not found content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '409': + description: Execution has not started yet content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/frontends/{frontendId}/deployments: + '503': + description: | + Durable execution is not available in this environment. Either the + capability is paused or this deployment cannot serve it, so the + request is not one to retry in a loop. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/frontends: get: tags: - Frontends - summary: List frontend deployments - operationId: listFrontendDeployments + summary: List all frontends in a project + description: | + Supports two mutually exclusive pagination modes. Offset mode uses `page` + and `limit` and returns `next` (URL). Cursor mode uses `cursor` and + `limit`, supports `search` (case-insensitive name match), and returns + `next_cursor`. Sending both `page` and `cursor` (or `page` and `search`) + returns 400. + operationId: listFrontends security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FrontendId' - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' responses: '200': description: Successful response content: application/json: schema: - $ref: '#/components/schemas/PaginatedFrontendDeployments' + $ref: '#/components/schemas/PaginatedFrontends' '400': - description: Bad request - invalid identifier + description: Bad request - invalid project identifier content: application/json: schema: @@ -4949,7 +5307,7 @@ paths: schema: $ref: '#/components/schemas/Error' '404': - description: Frontend not found + description: Project not found content: application/json: schema: @@ -4960,45 +5318,100 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/frontends/{frontendId}/usage: - get: + post: tags: - Frontends - summary: Per-day request and error counts for a single frontend + summary: Create a new frontend deployment description: | - Returns a zero-filled daily series of request counts and 5xx - error counts for one frontend, oldest first. Each entry is one - UTC day; missing days (no traffic recorded) come back as - `requests: 0, errors: 0` so the response always has exactly - `days` entries. - - Backs the Monitoring section on the Frontend detail page in - volcano-web. `days` defaults to 30 and is capped at 90 to keep - the (frontend_id, day) index scan bounded. - operationId: getFrontendUsageHistory - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/FrontendId' - - name: days - in: query - description: Number of trailing days to return (1–90, default 30). - required: false - schema: - type: integer - minimum: 1 - maximum: 90 - default: 30 + Creates and deploys a frontend for the project. + If a frontend with the same name already exists in the project, this operation updates that + frontend using the uploaded archive and starts a new deployment. A deployment that starts + immediately returns `status: provisioning`, then transitions to `active`, `degraded`, or + `failed`. If another deployment is running, the response preserves the frontend's current status + and exposes the queued deployment through `pending_deployment_id`. + Existing frontend traffic continues to use an available runtime while the new deployment builds + and provisions. Each deployment publishes its own static assets before the runtimes switch to its + build, and the live build's assets keep serving until the new deployment is live, so a page loaded + mid-deployment resolves its assets whichever build served it. A failed redeploy puts the runtimes + back on the build they were running, leaves the frontend `active` on the previous deployment, and + records the attempted deployment as failed. `degraded` means the runtime remains available but + edge synchronization requires recovery; Volcano retries the edge step without rebuilding. Only one deployment may run for a + given frontend, while independent frontends and projects can deploy concurrently. + For monorepos, provide `app_root` as a relative path from the uploaded archive root + to the Next.js app that should be built. Omit it for single-app archives. + Supported frontend environments are Next.js 15.x and 16.x with Node.js + 22.x or 24.x. The Node.js runtime is inferred from + `package.json` `engines.node`; if omitted, Volcano uses Node.js 22.x. + The selected Node.js family must also satisfy the installed Next.js package's + `engines.node` constraint. Volcano tests Next 15.5.26 (`^18.18.0 || ^19.8.0 || >=20.0.0`) and Next 16.3.6 (`>=20.9.0`). + Source archive size is enforced by the API with `SOURCE_ARCHIVE_SIZE_LIMIT_MB`; the CLI + does not apply its own source archive size limit. After the final container images are + built, the publish build enforces `LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MB` before pushing. + This operation is limited by plan-based frontend deployment quotas (`FREE_FRONTEND_DEPLOYMENTS`, `PRO_FRONTEND_DEPLOYMENTS`). + Each project can contain up to 10,000 frontends regardless of plan. + operationId: createFrontend + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: + - name + - archive + properties: + name: + type: string + maxLength: 63 + pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + description: DNS-safe frontend name + framework: + type: string + enum: + - nextjs + default: nextjs + description: Next.js frontend. Supported Next.js majors are 15.x and 16.x. + app_root: + type: string + maxLength: 1024 + description: Optional relative POSIX path from the uploaded archive root to the Next.js app to build, for example `apps/web`. + example: apps/web + variable_scope: + type: string + enum: + - all + - scoped + description: Variable selection for this deployment. New frontends default to `scoped`; omitting this field for an existing frontend preserves its current selection. + variables: + type: array + items: + type: string + pattern: ^[A-Za-z_][A-Za-z0-9_]*$ + description: Project variable names selected when `variable_scope` is `scoped`. Submit each name as a repeated multipart field. + archive: + type: string + format: binary + description: ZIP or tar.gz archive of the frontend project directory or monorepo workspace root. The API enforces SOURCE_ARCHIVE_SIZE_LIMIT_MB and stores a normalized tar.gz archive. responses: '200': - description: Successful response + description: Existing frontend updated; its deployment was started or queued content: application/json: schema: - $ref: '#/components/schemas/FrontendUsageHistoryResponse' + $ref: '#/components/schemas/Frontend' + '201': + description: Frontend created and deployment workflow started + content: + application/json: + schema: + $ref: '#/components/schemas/Frontend' '400': - description: Bad request - invalid identifier + description: Bad request content: application/json: schema: @@ -5010,13 +5423,19 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: Forbidden - project ownership required + description: Frontend deployment limit exceeded for the current plan or project hard cap content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Frontend not found + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: Conflict - frontend deletion is queued or running content: application/json: schema: @@ -5027,2654 +5446,2402 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/variables: + '503': + description: Service unavailable - frontend workflow or archive limit configuration missing + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/frontends/{frontendId}: get: tags: - - Variables - summary: List all variables for a project - description: | - Returns project-level environment variables used by deployed functions and frontends. - operationId: listVariables + - Frontends + summary: Get frontend details + operationId: getFrontend security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' + - $ref: '#/components/parameters/FrontendId' responses: '200': description: Successful response content: application/json: schema: - $ref: '#/components/schemas/PaginatedVariables' - '404': - description: Project not found + $ref: '#/components/schemas/Frontend' + '400': + description: Bad request - invalid identifier content: application/json: schema: $ref: '#/components/schemas/Error' - post: - tags: - - Variables - summary: Create or update a variable - description: | - Creates a project-level environment variable and triggers asynchronous propagation - to deployed functions and frontends in the project's configured regions. - operationId: createVariable - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateVariableRequest' - responses: - '201': - description: Variable created + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: - $ref: '#/components/schemas/Variable' - '400': - description: Bad request + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - project ownership required content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Private variable membership writes are disabled during rollout + '404': + description: Frontend not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases: - get: - tags: - - Databases - summary: List all databases for a project - description: | - Supports two mutually exclusive pagination modes. Offset mode uses `page` - and `limit`. Cursor mode uses `cursor` and `limit`, supports `search` - (case-insensitive name match), and returns `next_cursor`/`prev_cursor`. - The optional `status` filter applies in both modes and is bound to the - cursor. Sending both `page` and `cursor` (or `page` and `search`) returns 400. - operationId: listDatabases - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' - - name: status - in: query - required: false - schema: - type: string - enum: - - provisioning - - active - - restoring - - failed - - deleting - description: Return only the databases in this status. - responses: - '200': - description: Successful response + '500': + description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/PaginatedDatabases' - post: + $ref: '#/components/schemas/Error' + delete: tags: - - Databases - summary: Create a new serverless PostgreSQL database + - Frontends + summary: Delete a frontend description: | - Creates a serverless PostgreSQL database in the project. - Each project can hold 1 database on Free and up to 10,000 on Pro. - Requests over the plan's cap return 403. - operationId: createDatabase + Schedules asynchronous frontend deletion. If another deployment is running, the frontend + preserves its current status and exposes the queued deletion through `pending_deployment_id`. + Its status changes to `deleting` when cleanup starts. After cleanup, it returns 404 and no + longer appears in frontend lists. + operationId: deleteFrontend security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateDatabaseRequest' + - $ref: '#/components/parameters/FrontendId' responses: - '201': - description: Database created (provisioning) + '202': + description: Frontend deletion started or queued + '400': + description: Bad request - invalid identifier content: application/json: schema: - $ref: '#/components/schemas/Database' - '403': - description: Database limit exceeded for the project + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}: - get: - tags: - - Databases - summary: Get database details - operationId: getDatabase - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - responses: - '200': - description: Successful response + '403': + description: Forbidden - project ownership required content: application/json: schema: - $ref: '#/components/schemas/Database' - delete: - tags: - - Databases - summary: Delete a database - description: | - Deletes a database and the instance backing it. When the instance is - removed synchronously the database row is deleted and the response is - `204`. If the instance cannot be deleted right away, the database row - is retained (status `deleting`) and its teardown is handed to the - background reconciler, which retries the deletion and removes the row - once the instance is gone; in that case the response is `202`. The database row is - never dropped while its instance still exists, so an instance is - never orphaned without a record to retry from. - operationId: deleteDatabase - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - responses: - '202': - description: | - Deletion accepted and in progress. The backing instance could not be - removed synchronously, so the database is marked `deleting` and torn - down asynchronously by the reconciler. - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: deleting - message: - type: string - example: database deletion in progress - '204': - description: Database deleted (backing instance removed synchronously) + $ref: '#/components/schemas/Error' '404': - description: Project or database not found + description: Frontend not found content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: | - A restore is running on the database. Deleting it while a worker is - replacing its data would race that worker, so wait for the restore - to finish. + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: | - Volcano could not check whether a restore is running, and will not - delete a database that might be mid-restore. Retry. + description: Service unavailable - frontend workflow configuration missing content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/branches: - get: + /projects/{id}/frontends/{frontendId}/redeploy: + post: tags: - - Database Branches - summary: List a database's branches + - Frontends + summary: Redeploy frontend using latest uploaded artifact description: | - Returns every branch of the database, including those still provisioning - and those that failed, since each still holds a name. - - Connection strings are omitted. Fetch a single branch to get its - connection string. - operationId: listDatabaseBranches + Starts a new frontend workflow using the latest stored artifact. A deployment that starts + immediately returns `status: provisioning`, then transitions to `active`, `degraded`, or + `failed`. An overlapping deployment preserves the frontend's current status, is exposed through + `pending_deployment_id`, and supersedes any older queued deployment. The previous runtime and its + published static assets remain available during provisioning, and a failed redeploy restores the + regional runtimes to that build and keeps it serving while the attempted deployment is recorded as + failed. + operationId: redeployFrontend security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/FrontendId' responses: '200': - description: Successful response + description: Frontend redeploy started or queued content: application/json: schema: - $ref: '#/components/schemas/DatabaseBranchList' - '404': - description: Project or database not found + $ref: '#/components/schemas/Frontend' + '400': + description: Bad request - invalid identifier content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Branching is temporarily unavailable + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - post: - tags: - - Database Branches - summary: Create a branch of a database - description: | - Forks the database into a new branch. The branch starts as an exact copy - of the parent's data and diverges from there. - - Provisioning is asynchronous: the response is `202` with the branch in - `provisioning` and no connection string. Poll the branch until it reports - `active`, at which point it carries its own connection string. - - Retrying a create with a name that already exists returns `409` rather - than a second branch, so a retried request cannot silently consume two - slots of the branch allowance. - operationId: createDatabaseBranch - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateDatabaseBranchRequest' - responses: - '202': - description: Branch accepted and provisioning - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseBranch' - '400': - description: Invalid branch name or lifetime + '403': + description: Forbidden - project ownership required content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: | - The database has reached its branch allowance, or the owner's plan - does not include branching. + '404': + description: Frontend not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project or database not found + '409': + description: Conflict - frontend deletion is queued or running content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: | - A branch of that name already exists on this database, or the - database cannot be branched right now because it is still - provisioning, being restored, failed, or being deleted. + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Branching is temporarily unavailable + description: Service unavailable - frontend workflow configuration missing content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/branches/{branchName}: + /projects/{id}/frontends/{frontendId}/domain: get: tags: - - Database Branches - summary: Get a branch - description: | - Returns the branch, including its connection string once it is `active`. - Poll this after creating a branch to learn when it is connectable. - operationId: getDatabaseBranch + - Frontends + summary: Get frontend custom domain status + operationId: getFrontendCustomDomain security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' + - $ref: '#/components/parameters/FrontendId' responses: '200': - description: Successful response + description: | + Frontend custom domain status, or null when the frontend has no + custom domain configured (the common empty state). content: application/json: schema: - $ref: '#/components/schemas/DatabaseBranch' + nullable: true + allOf: + - $ref: '#/components/schemas/FrontendCustomDomainResponse' + '400': + description: Bad request - invalid identifier + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized - invalid or missing token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - project ownership required + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Project, database, or branch not found + description: Frontend not found content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Branching is temporarily unavailable + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - patch: + post: tags: - - Database Branches - summary: Extend a branch's lifetime + - Frontends + summary: Configure frontend custom domain (PRO) description: | - Replaces the branch's lifetime and restarts the countdown from now, so a - branch you are still working on is not swept mid-session. The new - duration is remembered, so a later reset re-arms the same lifetime. - operationId: updateDatabaseBranch + Configures one custom domain for a frontend. + The default Volcano-generated frontend URL remains active. + Wildcard Volcano frontend TLS remains valid and isolated from custom-domain certificate changes. + operationId: createFrontendCustomDomain security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' + - $ref: '#/components/parameters/FrontendId' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateDatabaseBranchRequest' + $ref: '#/components/schemas/CreateFrontendCustomDomainRequest' responses: '200': - description: Lifetime updated + description: Custom domain already configured with same hostname content: application/json: schema: - $ref: '#/components/schemas/DatabaseBranch' + $ref: '#/components/schemas/FrontendCustomDomainResponse' + '201': + description: Custom domain provisioning started + content: + application/json: + schema: + $ref: '#/components/schemas/FrontendCustomDomainResponse' '400': - description: Requested lifetime is outside the allowed range + description: Bad request - invalid domain + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized - invalid or missing token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - custom domains require PRO plan content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project, database, or branch not found + description: Frontend not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': - description: The branch is being deleted + description: Conflict - custom domain already in use, still detaching, or frontend already has a custom domain + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Branching is temporarily unavailable + description: Service unavailable - custom domain provisioning is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' delete: tags: - - Database Branches - summary: Delete a branch - description: | - Marks the branch for teardown and returns immediately. The branch stops - accepting connections at once; its fork and its row are removed by a - background job, so a provider outage cannot leave the call hanging or the - branch half-deleted. - - Deleting a branch that is still provisioning is allowed and stops the - build, and repeating the call while teardown is in progress is accepted - again. Once the branch is gone the call returns `404`. - operationId: deleteDatabaseBranch + - Frontends + summary: Delete frontend custom domain + operationId: deleteFrontendCustomDomain security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' + - $ref: '#/components/parameters/FrontendId' responses: - '202': - description: Deletion accepted and in progress + '204': + description: Custom domain detach scheduled + '400': + description: Bad request - invalid identifier content: application/json: schema: - type: object - properties: - status: - type: string - example: deleting - message: - type: string - example: branch deletion in progress - required: - - status - - message + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized - invalid or missing token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - project ownership required + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Project, database, or branch not found + description: Frontend or custom domain not found content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Branching is temporarily unavailable + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/branches/{branchName}/reset: - post: + /projects/{id}/frontends/{frontendId}/deployments: + get: tags: - - Database Branches - summary: Reset a branch to its parent's current state - description: | - Discards everything written on the branch and re-forks it from the - parent as it is now. - - Returns immediately with the branch in `provisioning`. The rewind runs in - the background; poll the branch until it reports `active` before - connecting again. - - The branch keeps its name and its connection string, so anything holding - that string keeps working once it is active again, and its lifetime is - re-armed to the duration it was created with. The branch does not serve - connections for the duration of the reset. - operationId: resetDatabaseBranch + - Frontends + summary: List frontend deployments + operationId: listFrontendDeployments security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' + - $ref: '#/components/parameters/FrontendId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' responses: - '202': - description: Branch reset accepted; the branch is provisioning + '200': + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/DatabaseBranch' - '404': - description: Project, database, or branch not found + $ref: '#/components/schemas/PaginatedFrontendDeployments' + '400': + description: Bad request - invalid identifier content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: | - The branch is not active, a reset is already in progress, the parent - database is being restored, or the parent was restored within the - last 24 hours — a reset re-forks from the parent, and the provider - holds a child's reset shut for that long afterwards. + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Branching is temporarily unavailable + '403': + description: Forbidden - project ownership required content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/branches/{branchName}/reset-password: - post: + '404': + description: Frontend not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/frontends/{frontendId}/usage: + get: tags: - - Database Branches - summary: Rotate a branch's password + - Frontends + summary: Per-day request and error counts for a single frontend description: | - Issues a new password for the branch and invalidates the previous - connection string. Existing connections are not interrupted; new ones - must use the returned string. Proxies pick the rotation up within a few - seconds, so the previous password can still open new connections until - then. + Returns a zero-filled daily series of request counts and 5xx + error counts for one frontend, oldest first. Each entry is one + UTC day; missing days (no traffic recorded) come back as + `requests: 0, errors: 0` so the response always has exactly + `days` entries. - The parent database's credentials are untouched. - operationId: resetDatabaseBranchPassword + Backs the Monitoring section on the Frontend detail page in + volcano-web. `days` defaults to 30 and is capped at 90 to keep + the (frontend_id, day) index scan bounded. + operationId: getFrontendUsageHistory security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' + - $ref: '#/components/parameters/FrontendId' + - name: days + in: query + description: Number of trailing days to return (1–90, default 30). + required: false + schema: + type: integer + minimum: 1 + maximum: 90 + default: 30 responses: '200': - description: Password rotated + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/DatabaseBranch' - '404': - description: Project, database, or branch not found + $ref: '#/components/schemas/FrontendUsageHistoryResponse' + '400': + description: Bad request - invalid identifier content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: The branch is not active + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Branching is temporarily unavailable + '403': + description: Forbidden - project ownership required content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/backups: + '404': + description: Frontend not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/frontends/{frontendId}/function-routes: + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/FrontendId' get: tags: - - Database Backups - summary: List a database's backups - description: | - Returns every backup of the database, newest first, together with the - window a point-in-time restore may target. - - Both backups you took and backups the schedule produced are listed; - `source` tells them apart. Only manual backups count against the plan's - backup allowance. - operationId: listDatabaseBackups + - Frontends + summary: List a Frontend's Function routes + operationId: listFrontendFunctionRoutes security: - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' + - ProjectAccessToken: [] responses: '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseBackupList' - '403': - description: Backups are PRO-only and the owner's plan does not include them + description: Function routes ordered from most to least specific content: application/json: schema: - $ref: '#/components/schemas/Error' - '404': - description: Project or database not found + $ref: '#/components/schemas/FrontendFunctionRouteList' + '401': + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: | - The database has no storage project yet, so there is nothing to - list. A database reports this while it is still provisioning. + '403': + description: Project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Backups are temporarily unavailable + '500': + description: Failed to list Function routes content: application/json: schema: $ref: '#/components/schemas/Error' post: tags: - - Database Backups - summary: Back up a database - description: | - Captures the database as it is now. The backup is available immediately; - its `size_bytes` appears once the storage provider has costed it. - - Backups are rate-limited to one per minute per database, and capped by - the owner's plan. - operationId: createDatabaseBackup + - Frontends + summary: Route a Frontend path to an HTTP Function + operationId: createFrontendFunctionRoute + description: The Frontend and Function must belong to this Project. The Function may be private but must use HTTP invocation mode. The route applies to every hostname that resolves to the Frontend, including generated, custom-domain, preview, and local hostnames. security: - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' + - ProjectAccessToken: [] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/CreateDatabaseBackupRequest' + $ref: '#/components/schemas/CreateFrontendFunctionRouteRequest' responses: '201': - description: Backup created + description: Function route created content: application/json: schema: - $ref: '#/components/schemas/DatabaseBackup' + $ref: '#/components/schemas/FrontendFunctionRoute' '400': - description: Invalid backup name + description: Invalid path or non-HTTP Function + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: | - The database has reached its backup allowance, or the owner's plan - does not include backups, which are PRO-only. + description: Project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project or database not found + description: Frontend or Function not found in the Project content: application/json: schema: $ref: '#/components/schemas/Error' '409': - description: | - A backup of that name already exists, the database is not active, a - restore is running on it, or a backup was taken too recently. + description: Path is already routed or the Frontend has reached its 64-route limit content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Backups are temporarily unavailable + '500': + description: Failed to create Function route content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/backups/{backupName}: - get: + /projects/{id}/frontends/{frontendId}/function-routes/{routeId}: + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/FrontendId' + - $ref: '#/components/parameters/FrontendFunctionRouteId' + put: tags: - - Database Backups - summary: Get a backup - description: Returns one backup of the database. - operationId: getDatabaseBackup + - Frontends + summary: Replace a Frontend Function route + operationId: updateFrontendFunctionRoute security: - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BackupName' - responses: - '200': - description: Successful response + - ProjectAccessToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFrontendFunctionRouteRequest' + responses: + '200': + description: Function route replaced content: application/json: schema: - $ref: '#/components/schemas/DatabaseBackup' + $ref: '#/components/schemas/FrontendFunctionRoute' + '400': + description: Invalid path or non-HTTP Function + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '403': - description: Backups are PRO-only and the owner's plan does not include them + description: Project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project, database, or backup not found + description: Function route, Frontend, or Function not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': - description: | - The database has no storage project yet, so it holds no backups. A - database reports this while it is still provisioning. + description: Path is already routed or the Frontend has reached its 64-route limit content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Backups are temporarily unavailable + '500': + description: Failed to replace Function route content: application/json: schema: $ref: '#/components/schemas/Error' delete: tags: - - Database Backups - summary: Delete a backup - description: | - Deletes the backup and frees its storage. Scheduled backups can be - deleted too. A backup that is already gone reports `404`, so a name - that never existed and a name that no longer does read the same. - Refused with `409` while the database is being restored. - operationId: deleteDatabaseBackup + - Frontends + summary: Delete a Frontend Function route + operationId: deleteFrontendFunctionRoute security: - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BackupName' + - ProjectAccessToken: [] responses: - '200': - description: Backup deleted + '204': + description: Function route deleted + '401': + description: Unauthorized content: application/json: schema: - type: object - properties: - status: - type: string - example: deleted - message: - type: string - example: backup deleted - required: - - status - - message + $ref: '#/components/schemas/Error' '403': - description: Backups are PRO-only and the owner's plan does not include them + description: Project not owned by the caller content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project, database, or backup not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: | - The database is being restored. A restore is pinned to a backup it - may not have restored yet, so deleting one is refused until the - restore finishes. + description: Function route not found on the Frontend content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Backups are temporarily unavailable + '500': + description: Failed to delete Function route content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/backup-schedule: + /projects/{id}/variables: get: tags: - - Database Backups - summary: Get the automated backup schedule + - Variables + summary: List all variables for a project description: | - Returns the database's backup schedule. An empty list means no scheduled - backups. - operationId: getDatabaseBackupSchedule + Returns project-level environment variables used by deployed functions and frontends. + operationId: listVariables security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' responses: '200': description: Successful response content: application/json: schema: - $ref: '#/components/schemas/DatabaseBackupSchedule' - '403': - description: Backups are PRO-only and the owner's plan does not include them - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/PaginatedVariables' '404': - description: Project or database not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: | - The database has no storage project yet, so it has no schedule. A - database reports this while it is still provisioning. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '503': - description: Backups are temporarily unavailable + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - put: + post: tags: - - Database Backups - summary: Replace the automated backup schedule + - Variables + summary: Create or update a variable description: | - Replaces the schedule wholesale. Send an empty `entries` list to stop - scheduled backups. - - Scheduled backups do not count against the plan's backup allowance, but - their retention is clamped to the plan's. - operationId: updateDatabaseBackupSchedule + Creates a project-level environment variable and triggers asynchronous propagation + to deployed functions and frontends in the project's configured regions. + operationId: createVariable security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/DatabaseBackupSchedule' + $ref: '#/components/schemas/CreateVariableRequest' responses: - '200': - description: Schedule replaced + '201': + description: Variable created content: application/json: schema: - $ref: '#/components/schemas/DatabaseBackupSchedule' + $ref: '#/components/schemas/Variable' '400': - description: | - The schedule names a recurrence that cannot fire: a weekly or - monthly one with no `day`, or a `day` outside its frequency's range - (1-7 for weekly, 1-28 for monthly). The response says which. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Backups are PRO-only and the owner's plan does not include them - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '404': - description: Project or database not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: | - The database is not active, or a restore is running on it — a restore - moves the data to a new branch, and the provider keeps the schedule - per branch. + description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Backups are temporarily unavailable + description: Private variable membership writes are disabled during rollout content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/restores: + /projects/{id}/databases: get: tags: - - Database Backups - summary: List a database's restores + - Databases + summary: List all databases for a project description: | - Returns the database's restore history, newest first, capped at the 50 - most recent. There is no pagination: a database that has been restored - more than 50 times keeps the older records but does not return them. - operationId: listDatabaseRestores + Supports two mutually exclusive pagination modes. Offset mode uses `page` + and `limit`. Cursor mode uses `cursor` and `limit`, supports `search` + (case-insensitive name match), and returns `next_cursor`/`prev_cursor`. + The optional `status` filter applies in both modes and is bound to the + cursor. Sending both `page` and `cursor` (or `page` and `search`) returns 400. + operationId: listDatabases security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' + - name: status + in: query + required: false + schema: + type: string + enum: + - provisioning + - active + - restoring + - failed + - deleting + description: Return only the databases in this status. responses: '200': description: Successful response content: application/json: schema: - $ref: '#/components/schemas/DatabaseRestoreList' - '403': - description: Backups are PRO-only and the owner's plan does not include them - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '404': - description: Project or database not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '503': - description: Backups are temporarily unavailable - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/PaginatedDatabases' post: tags: - - Database Backups - summary: Restore a database + - Databases + summary: Create a new serverless PostgreSQL database description: | - Replaces the database's data, either with a named backup or with its - state at a point in time. This is destructive: everything written after - that point is discarded. - - Asynchronous: the response is `202` with the restore `pending` and the - database `restoring`. The database does not accept connections until the - restore reports `completed`; its connection string is unchanged - throughout, so nothing holding it needs updating. - - Restores are in place. There is no way to restore into a second - database, and a database's branches are never restored — they keep - serving their own data, but resetting a branch from its parent is - refused by the storage provider for up to 24 hours afterwards. - operationId: createDatabaseRestore + Creates a serverless PostgreSQL database in the project. + Each project can hold 1 database on Free and up to 10,000 on Pro. + Requests over the plan's cap return 403. + operationId: createDatabase security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/DatabaseName' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/CreateDatabaseRestoreRequest' + $ref: '#/components/schemas/CreateDatabaseRequest' responses: - '202': - description: Restore accepted and in progress + '201': + description: Database created (provisioning) content: application/json: schema: - $ref: '#/components/schemas/DatabaseRestore' - '400': - description: | - Neither or both restore targets were named, or the requested time is - outside the available window. + $ref: '#/components/schemas/Database' + '403': + description: Database limit exceeded for the project content: application/json: schema: $ref: '#/components/schemas/Error' - '403': + /projects/{id}/databases/{databaseName}: + get: + tags: + - Databases + summary: Get database details + operationId: getDatabase + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DatabaseName' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/Database' + delete: + tags: + - Databases + summary: Delete a database + description: | + Deletes a database and the instance backing it. When the instance is + removed synchronously the database row is deleted and the response is + `204`. If the instance cannot be deleted right away, the database row + is retained (status `deleting`) and its teardown is handed to the + background reconciler, which retries the deletion and removes the row + once the instance is gone; in that case the response is `202`. The database row is + never dropped while its instance still exists, so an instance is + never orphaned without a record to retry from. + operationId: deleteDatabase + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DatabaseName' + responses: + '202': description: | - The owner's plan does not include backups or point-in-time restore. - Both are PRO-only. + Deletion accepted and in progress. The backing instance could not be + removed synchronously, so the database is marked `deleting` and torn + down asynchronously by the reconciler. content: application/json: schema: - $ref: '#/components/schemas/Error' + type: object + properties: + status: + type: string + example: deleting + message: + type: string + example: database deletion in progress + '204': + description: Database deleted (backing instance removed synchronously) '404': - description: Project, database, or backup not found + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: | - A restore is already in progress, the database is not active, - another database operation is still running, or the database is - holding as many pre-restore branches as it may. + A restore is running on the database. Deleting it while a worker is + replacing its data would race that worker, so wait for the restore + to finish. content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Backups are temporarily unavailable + description: | + Volcano could not check whether a restore is running, and will not + delete a database that might be mid-restore. Retry. content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/restores/{restoreId}: + /projects/{id}/databases/{databaseName}/branches: get: tags: - - Database Backups - summary: Get a restore + - Database Branches + summary: List a database's branches description: | - Returns the restore. Poll this after starting one; the database is - connectable again once it reports `completed`. - operationId: getDatabaseRestore + Returns every branch of the database, including those still provisioning + and those that failed, since each still holds a name. + + Connection strings are omitted. Fetch a single branch to get its + connection string. + operationId: listDatabaseBranches security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/RestoreId' responses: '200': description: Successful response content: application/json: schema: - $ref: '#/components/schemas/DatabaseRestore' - '403': - description: Backups are PRO-only and the owner's plan does not include them - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/DatabaseBranchList' '404': - description: Project, database, or restore not found + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Backups are temporarily unavailable + description: Branching is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/reset-password: post: tags: - - Databases - summary: Reset database password + - Database Branches + summary: Create a branch of a database description: | - Rotates the Volcano-managed PostgreSQL password used by clients when connecting - through pgproxy. This does not rotate or expose the internal owner password. - The returned password and connection string are the only client credentials that - will authenticate through pgproxy after reset. + Forks the database into a new branch. The branch starts as an exact copy + of the parent's data and diverges from there. - Existing connections are not interrupted; new ones must use the returned - string. Proxies pick the rotation up within a few seconds, so the previous - password can still open new connections until then. - operationId: resetDatabasePassword + Provisioning is asynchronous: the response is `202` with the branch in + `provisioning` and no connection string. Poll the branch until it reports + `active`, at which point it carries its own connection string. + + Retrying a create with a name that already exists returns `409` rather + than a second branch, so a retried request cannot silently consume two + slots of the branch allowance. + operationId: createDatabaseBranch security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateDatabaseBranchRequest' responses: - '200': - description: Password reset successful + '202': + description: Branch accepted and provisioning content: application/json: schema: - type: object - properties: - message: - type: string - role_name: - type: string - description: Volcano-managed per-database client login (also the pgproxy routing username) - example: volcano_client_11111111-1111-1111-1111-111111111111 - new_password: - type: string - description: New Volcano-managed client password. Always starts with `vpg_`. - connection_string: - type: string - description: Updated pgproxy connection string using Volcano-managed credentials. + $ref: '#/components/schemas/DatabaseBranch' '400': - description: Database is not active + description: Invalid branch name or lifetime + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: | + The database has reached its branch allowance, or the owner's plan + does not include branching. content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Database not found + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: | - A restore is running on the database. A restore replaces the - credentials as it finishes, so wait for it and rotate afterwards. + A branch of that name already exists on this database, or the + database cannot be branched right now because it is still + provisioning, being restored, failed, or being deleted. content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: | - Volcano could not check whether a restore is running, and will not - rotate a credential a restore might be about to replace. Retry. + description: Branching is temporarily unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/databases/{databaseName}/branches/{branchName}: + get: + tags: + - Database Branches + summary: Get a branch + description: | + Returns the branch, including its connection string once it is `active`. + Poll this after creating a branch to learn when it is connectable. + operationId: getDatabaseBranch + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/BranchName' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/DatabaseBranch' + '404': + description: Project, database, or branch not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: Branching is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/type: patch: tags: - - Databases - summary: Update database size + - Database Branches + summary: Extend a branch's lifetime description: | - Change the size tier of a database. This may briefly interrupt active connections. - - **Available sizes:** - - `volcano-db-xs`: Up to ~1GB RAM - Development, small apps - - `volcano-db-s`: Up to ~4GB RAM - Production-ready, light traffic - - `volcano-db-m`: Up to ~8GB RAM - Medium traffic applications - - `volcano-db-l`: Up to ~16GB RAM - High traffic, larger datasets - - `volcano-db-xl`: Up to ~32GB RAM - Heavy workloads - - `volcano-db-2xl`: Up to ~64GB RAM - Enterprise-scale - operationId: updateDatabaseType + Replaces the branch's lifetime and restarts the countdown from now, so a + branch you are still working on is not swept mid-session. The new + duration is remembered, so a later reset re-arms the same lifetime. + operationId: updateDatabaseBranch security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/BranchName' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateDatabaseTypeRequest' + $ref: '#/components/schemas/UpdateDatabaseBranchRequest' responses: '200': - description: Database type updated + description: Lifetime updated content: application/json: schema: - $ref: '#/components/schemas/Database' + $ref: '#/components/schemas/DatabaseBranch' '400': - description: Invalid database type + description: Requested lifetime is outside the allowed range + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Database not found + description: Project, database, or branch not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '409': - description: | - The database is not active — being provisioned, deleted, or - restored. Compute can only be changed while it is active. + description: The branch is being deleted content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: | - Volcano could not check whether a restore is running, and will not - reconfigure compute a restore might be moving. Retry. + description: Branching is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/databases/{databaseName}/stats: - get: + delete: tags: - - Databases - summary: Get database consumption metrics + - Database Branches + summary: Delete a branch description: | - Retrieve consumption metrics including storage, compute time, and data transfer. - Metrics are aggregated at the project level. Defaults to last 24 hours. + Marks the branch for teardown and returns immediately. The branch stops + accepting connections at once; its fork and its row are removed by a + background job, so a provider outage cannot leave the call hanging or the + branch half-deleted. - **Note:** Advanced metrics require an upgraded plan. - operationId: getDatabaseStats + Deleting a branch that is still provisioning is allowed and stops the + build, and repeating the call while teardown is in progress is accepted + again. Once the branch is gone the call returns `404`. + operationId: deleteDatabaseBranch security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - - name: from - in: query - required: false - schema: - type: string - format: date-time - description: Start time in RFC3339 format (e.g., "2024-01-01T00:00:00Z"). Defaults to 24 hours ago. - - name: to - in: query - required: false - schema: - type: string - format: date-time - description: End time in RFC3339 format (e.g., "2024-01-02T00:00:00Z"). Defaults to now. - - name: granularity - in: query - required: false - schema: - type: string - enum: - - hourly - - daily - - monthly - default: hourly - description: Level of detail for metrics aggregation + - $ref: '#/components/parameters/BranchName' responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseStats' - '400': - description: Invalid parameters + '202': + description: Deletion accepted and in progress content: application/json: schema: - $ref: '#/components/schemas/Error' + type: object + properties: + status: + type: string + example: deleting + message: + type: string + example: branch deletion in progress + required: + - status + - message '404': - description: Database not found + description: Project, database, or branch not found content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Database metrics not available or requires upgraded plan + description: Branching is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - /databases/{databaseName}/query/ping: + /projects/{id}/databases/{databaseName}/branches/{branchName}/reset: post: tags: - - Database Queries - summary: Database connectivity probe (REST API) + - Database Branches + summary: Reset a branch to its parent's current state description: | - Connectivity probe that runs a fixed `SELECT 1` through pgproxy, using the - same authentication, status/bandwidth gating, and metering as the other - `/query/*` endpoints. + Discards everything written on the branch and re-forks it from the + parent as it is now. - Unlike those endpoints, ping takes **no request body** and performs **no - table-name validation**, so it works on any database — including a freshly - provisioned, empty one. It is a real committed round-trip through pgproxy, - so a `200` means the database is reachable and queryable. Used by the - dashboard's database connection test. - operationId: queryDatabasePing + Returns immediately with the branch in `provisioning`. The rewind runs in + the background; poll the branch until it reports `active` before + connecting again. + + The branch keeps its name and its connection string, so anything holding + that string keeps working once it is active again, and its lifetime is + re-armed to the duration it was created with. The branch does not serve + connections for the duration of the reset. + operationId: resetDatabaseBranch security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/BranchName' responses: - '200': - description: Database is reachable + '202': + description: Branch reset accepted; the branch is provisioning content: application/json: schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - '?column?': 1 - count: 1 - '401': - description: Not authenticated + $ref: '#/components/schemas/DatabaseBranch' + '404': + description: Project, database, or branch not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '409': + description: | + The branch is not active, a reset is already in progress, the parent + database is being restored, or the parent was restored within the + last 24 hours — a reset re-forks from the parent, and the provider + holds a child's reset shut for that long afterwards. content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database not found + '503': + description: Branching is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' - /databases/{databaseName}/query/select: + /projects/{id}/databases/{databaseName}/branches/{branchName}/reset-password: post: tags: - - Database Queries - summary: Query database with SELECT (REST API) + - Database Branches + summary: Rotate a branch's password description: | - Query your database using a simple REST API - no SQL required! - - **Authentication:** Requires auth user access token (from signup/signin) - - **Row-Level Security:** Automatically enforced - you see only data you have access to - - **Use Cases:** - - Query from browser/mobile apps - - Simple data retrieval - - Filtered searches with sorting and pagination + Issues a new password for the branch and invalidates the previous + connection string. Existing connections are not interrupted; new ones + must use the returned string. Proxies pick the rotation up within a few + seconds, so the previous password can still open new connections until + then. - **Note:** For complex queries (JOINs, CTEs), use functions with direct SQL - operationId: queryDatabaseSelect + The parent database's credentials are untouched. + operationId: resetDatabaseBranchPassword security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseSelectRequest' + - $ref: '#/components/parameters/BranchName' responses: '200': - description: Query successful - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - id: uuid-123 - title: My Post - content: Post content - status: published - views: 150 - created_at: '2026-01-13T10:00:00Z' - count: 1 - '400': - description: Invalid query + description: Password rotated content: application/json: schema: - $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + $ref: '#/components/schemas/DatabaseBranch' + '404': + description: Project, database, or branch not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '409': + description: The branch is not active content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database not found + '503': + description: Branching is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' - /databases/{databaseName}/query/insert: - post: + /projects/{id}/databases/{databaseName}/backups: + get: tags: - - Database Queries - summary: Insert data into database (REST API) + - Database Backups + summary: List a database's backups description: | - Insert new rows into your database using REST API. - - **Authentication:** Requires auth user access token - - **Auto-set user_id:** If your table has a trigger using `auth.uid()`, - user_id will be automatically set to the authenticated user + Returns every backup of the database, newest first, together with the + window a point-in-time restore may target. - **Security:** Row-Level Security policies are enforced - operationId: queryDatabaseInsert + Both backups you took and backups the schedule produced are listed; + `source` tells them apart. Only manual backups count against the plan's + backup allowance. + operationId: listDatabaseBackups security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseInsertRequest' responses: '200': - description: Insert successful + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - id: uuid-123 - title: My New Post - content: This is the content - status: draft - user_id: user-uuid - created_at: '2026-01-13T10:00:00Z' - count: 1 - '400': - description: Invalid request + $ref: '#/components/schemas/DatabaseBackupList' + '403': + description: Backups are PRO-only and the owner's plan does not include them content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '404': + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '409': + description: | + The database has no storage project yet, so there is nothing to + list. A database reports this while it is still provisioning. content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database not found + '503': + description: Backups are temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' - /databases/{databaseName}/query/update: post: tags: - - Database Queries - summary: Update data in database (REST API) + - Database Backups + summary: Back up a database description: | - Update existing rows in your database using REST API. - - **Security:** Row-Level Security ensures you can only update data you have access to - - **Safety:** Requires at least one filter to prevent accidental mass updates. A - request with no `filters` is rejected with `400` (mirrors delete). This matters - for service-key queries, which run with full access and bypass RLS. + Captures the database as it is now. The backup is available immediately; + its `size_bytes` appears once the storage provider has costed it. - **Note:** If RLS blocks the update, an empty result is returned (not an error) - operationId: queryDatabaseUpdate + Backups are rate-limited to one per minute per database, and capped by + the owner's plan. + operationId: createDatabaseBackup security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/DatabaseUpdateRequest' + $ref: '#/components/schemas/CreateDatabaseBackupRequest' responses: - '200': - description: Update successful + '201': + description: Backup created content: application/json: schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - id: post-uuid - title: Updated Title - status: published - updated_at: '2026-01-13T10:05:00Z' - count: 1 + $ref: '#/components/schemas/DatabaseBackup' '400': - description: Invalid request + description: Invalid backup name content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '403': + description: | + The database has reached its backup allowance, or the owner's plan + does not include backups, which are PRO-only. content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '404': + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database not found + '409': + description: | + A backup of that name already exists, the database is not active, a + restore is running on it, or a backup was taken too recently. content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' - /databases/{databaseName}/query/delete: - post: + '503': + description: Backups are temporarily unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/databases/{databaseName}/backups/{backupName}: + get: tags: - - Database Queries - summary: Delete data from database (REST API) - description: | - Delete rows from your database using REST API. - - **Safety:** Requires at least one filter to prevent accidental mass deletions - - **Security:** Row-Level Security ensures you can only delete data you have access to - - **Note:** If RLS blocks the delete, an empty result is returned (not an error) - operationId: queryDatabaseDelete + - Database Backups + summary: Get a backup + description: Returns one backup of the database. + operationId: getDatabaseBackup security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseDeleteRequest' + - $ref: '#/components/parameters/BackupName' responses: '200': - description: Delete successful + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - id: post-uuid - title: Deleted Post - count: 1 - '400': - description: Invalid request or missing filters + $ref: '#/components/schemas/DatabaseBackup' + '403': + description: Backups are PRO-only and the owner's plan does not include them content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '404': + description: Project, database, or backup not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '409': + description: | + The database has no storage project yet, so it holds no backups. A + database reports this while it is still provisioning. content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database not found + '503': + description: Backups are temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' - /databases/{databaseName}/branches/{branchName}/query/ping: - post: + delete: tags: - - Database Queries - summary: Database connectivity probe (REST API) + - Database Backups + summary: Delete a backup description: | - Connectivity probe that runs a fixed `SELECT 1` through pgproxy, using the - same authentication, status/bandwidth gating, and metering as the other - `/query/*` endpoints. - - Unlike those endpoints, ping takes **no request body** and performs **no - table-name validation**, so it works on any database — including a freshly - provisioned, empty one. It is a real committed round-trip through pgproxy, - so a `200` means the database is reachable and queryable. Used by the - dashboard's database connection test. - - **Branch-targeted.** Runs against the named branch instead of the parent - database, using the branch's own credentials. The branch must be `active` - and unexpired. Nothing about this request can reach the parent's data. - operationId: queryDatabaseBranchPing + Deletes the backup and frees its storage. Scheduled backups can be + deleted too. A backup that is already gone reports `404`, so a name + that never existed and a name that no longer does read the same. + Refused with `409` while the database is being restored. + operationId: deleteDatabaseBackup security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' + - $ref: '#/components/parameters/BackupName' responses: '200': - description: Database is reachable + description: Backup deleted content: application/json: schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - '?column?': 1 - count: 1 - '401': - description: Not authenticated + type: object + properties: + status: + type: string + example: deleted + message: + type: string + example: backup deleted + required: + - status + - message + '403': + description: Backups are PRO-only and the owner's plan does not include them content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '404': + description: Project, database, or backup not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database or branch not found + '409': + description: | + The database is being restored. A restore is pinned to a backup it + may not have restored yet, so deleting one is refused until the + restore finishes. content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' '503': - $ref: '#/components/responses/DatabaseBranchQueryUnavailable' - /databases/{databaseName}/branches/{branchName}/query/select: - post: + description: Backups are temporarily unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/databases/{databaseName}/backup-schedule: + get: tags: - - Database Queries - summary: Query database with SELECT (REST API) + - Database Backups + summary: Get the automated backup schedule description: | - Query your database using a simple REST API - no SQL required! - - **Authentication:** Requires auth user access token (from signup/signin) - - **Row-Level Security:** Automatically enforced - you see only data you have access to - - **Use Cases:** - - Query from browser/mobile apps - - Simple data retrieval - - Filtered searches with sorting and pagination - - **Note:** For complex queries (JOINs, CTEs), use Lambda functions with direct SQL - - **Branch-targeted.** Runs against the named branch instead of the parent - database, using the branch's own credentials. The branch must be `active` - and unexpired. Nothing about this request can reach the parent's data. - operationId: queryDatabaseBranchSelect + Returns the database's backup schedule. An empty list means no scheduled + backups. + operationId: getDatabaseBackupSchedule security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseSelectRequest' responses: '200': - description: Query successful + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - id: uuid-123 - title: My Post - content: Post content - status: published - views: 150 - created_at: '2026-01-13T10:00:00Z' - count: 1 - '400': - description: Invalid query + $ref: '#/components/schemas/DatabaseBackupSchedule' + '403': + description: Backups are PRO-only and the owner's plan does not include them content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '404': + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '409': + description: | + The database has no storage project yet, so it has no schedule. A + database reports this while it is still provisioning. content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database or branch not found + '503': + description: Backups are temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' - '503': - $ref: '#/components/responses/DatabaseBranchQueryUnavailable' - /databases/{databaseName}/branches/{branchName}/query/insert: - post: + put: tags: - - Database Queries - summary: Insert data into database (REST API) + - Database Backups + summary: Replace the automated backup schedule description: | - Insert new rows into your database using REST API. - - **Authentication:** Requires auth user access token - - **Auto-set user_id:** If your table has a trigger using `auth.uid()`, - user_id will be automatically set to the authenticated user - - **Security:** Row-Level Security policies are enforced + Replaces the schedule wholesale. Send an empty `entries` list to stop + scheduled backups. - **Branch-targeted.** Runs against the named branch instead of the parent - database, using the branch's own credentials. The branch must be `active` - and unexpired. Nothing about this request can reach the parent's data. - operationId: queryDatabaseBranchInsert + Scheduled backups do not count against the plan's backup allowance, but + their retention is clamped to the plan's. + operationId: updateDatabaseBackupSchedule security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/DatabaseInsertRequest' + $ref: '#/components/schemas/DatabaseBackupSchedule' responses: '200': - description: Insert successful + description: Schedule replaced content: application/json: schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - id: uuid-123 - title: My New Post - content: This is the content - status: draft - user_id: user-uuid - created_at: '2026-01-13T10:00:00Z' - count: 1 + $ref: '#/components/schemas/DatabaseBackupSchedule' '400': - description: Invalid request + description: | + The schedule names a recurrence that cannot fire: a weekly or + monthly one with no `day`, or a `day` outside its frequency's range + (1-7 for weekly, 1-28 for monthly). The response says which. content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '403': + description: Backups are PRO-only and the owner's plan does not include them content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '404': + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database or branch not found + '409': + description: | + The database is not active, or a restore is running on it — a restore + moves the data to a new branch, and the provider keeps the schedule + per branch. content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' '503': - $ref: '#/components/responses/DatabaseBranchQueryUnavailable' - /databases/{databaseName}/branches/{branchName}/query/update: - post: + description: Backups are temporarily unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/databases/{databaseName}/restores: + get: tags: - - Database Queries - summary: Update data in database (REST API) + - Database Backups + summary: List a database's restores description: | - Update existing rows in your database using REST API. - - **Security:** Row-Level Security ensures you can only update data you have access to - - **Safety:** Requires at least one filter to prevent accidental mass updates. A - request with no `filters` is rejected with `400` (mirrors delete). This matters - for service-key queries, which run with full access and bypass RLS. - - **Note:** If RLS blocks the update, an empty result is returned (not an error) - - **Branch-targeted.** Runs against the named branch instead of the parent - database, using the branch's own credentials. The branch must be `active` - and unexpired. Nothing about this request can reach the parent's data. - operationId: queryDatabaseBranchUpdate + Returns the database's restore history, newest first, capped at the 50 + most recent. There is no pagination: a database that has been restored + more than 50 times keeps the older records but does not return them. + operationId: listDatabaseRestores security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseUpdateRequest' responses: '200': - description: Update successful - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - id: post-uuid - title: Updated Title - status: published - updated_at: '2026-01-13T10:05:00Z' - count: 1 - '400': - description: Invalid request + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + $ref: '#/components/schemas/DatabaseRestoreList' + '403': + description: Backups are PRO-only and the owner's plan does not include them content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '404': + description: Project or database not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database or branch not found + '503': + description: Backups are temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' - '503': - $ref: '#/components/responses/DatabaseBranchQueryUnavailable' - /databases/{databaseName}/branches/{branchName}/query/delete: post: tags: - - Database Queries - summary: Delete data from database (REST API) + - Database Backups + summary: Restore a database description: | - Delete rows from your database using REST API. - - **Safety:** Requires at least one filter to prevent accidental mass deletions - - **Security:** Row-Level Security ensures you can only delete data you have access to + Replaces the database's data, either with a named backup or with its + state at a point in time. This is destructive: everything written after + that point is discarded. - **Note:** If RLS blocks the delete, an empty result is returned (not an error) + Asynchronous: the response is `202` with the restore `pending` and the + database `restoring`. The database does not accept connections until the + restore reports `completed`; its connection string is unchanged + throughout, so nothing holding it needs updating. - **Branch-targeted.** Runs against the named branch instead of the parent - database, using the branch's own credentials. The branch must be `active` - and unexpired. Nothing about this request can reach the parent's data. - operationId: queryDatabaseBranchDelete + Restores are in place. There is no way to restore into a second + database, and a database's branches are never restored — they keep + serving their own data, but resetting a branch from its parent is + refused by the storage provider for up to 24 hours afterwards. + operationId: createDatabaseRestore security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: + - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/DatabaseName' - - $ref: '#/components/parameters/BranchName' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/DatabaseDeleteRequest' + $ref: '#/components/schemas/CreateDatabaseRestoreRequest' responses: - '200': - description: Delete successful + '202': + description: Restore accepted and in progress content: application/json: schema: - $ref: '#/components/schemas/DatabaseQueryResult' - example: - data: - - id: post-uuid - title: Deleted Post - count: 1 + $ref: '#/components/schemas/DatabaseRestore' '400': - description: Invalid request or missing filters + description: | + Neither or both restore targets were named, or the requested time is + outside the available window. content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '403': + description: | + The owner's plan does not include backups or point-in-time restore. + Both are PRO-only. content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '404': + description: Project, database, or backup not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Database or branch not found + '409': + description: | + A restore is already in progress, the database is not active, + another database operation is still running, or the database is + holding as many pre-restore branches as it may. content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - $ref: '#/components/responses/DatabaseQueryCapExceeded' '503': - $ref: '#/components/responses/DatabaseBranchQueryUnavailable' - /databases/regions: + description: Backups are temporarily unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/databases/{databaseName}/restores/{restoreId}: get: tags: - - Databases - summary: List platform-supported regions for database provisioning - operationId: listDatabaseRegions + - Database Backups + summary: Get a restore description: | - Returns the regions enabled for database provisioning in this platform environment. - These are the same regions offered for function deployment, and the only values - the `region` field of a database accepts. - This is a public endpoint that doesn't require authentication. + Returns the restore. Poll this after starting one; the database is + connectable again once it reports `completed`. + operationId: getDatabaseRestore + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/RestoreId' responses: '200': - description: List of platform-supported regions + description: Successful response content: application/json: schema: - type: array - items: - type: object - properties: - id: - type: string - example: aws-us-east-1 - description: Region identifier for API usage - name: - type: string - example: US East (N. Virginia) - description: Human-readable region location - /databases/postgres-versions: - get: - tags: - - Databases - summary: List available PostgreSQL versions - operationId: listPostgresVersions - description: | - Returns a list of supported PostgreSQL major versions for database provisioning. - This is a public endpoint that doesn't require authentication. - responses: - '200': - description: List of available PostgreSQL versions + $ref: '#/components/schemas/DatabaseRestore' + '403': + description: Backups are PRO-only and the owner's plan does not include them content: application/json: schema: - type: array - items: - type: object - properties: - version: - type: string - example: '16' - description: PostgreSQL major version number - name: - type: string - example: PostgreSQL 16 - description: Human-readable version name - default: - type: boolean - description: Whether this is the default version (recommended) - deprecated: - type: boolean - description: Whether this version is deprecated (approaching EOL) - /functions/runtimes: - get: - tags: - - Functions - summary: List supported function runtimes - operationId: listFunctionRuntimes - security: [] - description: | - Returns the public function runtime catalog used by CLI clients to select supported runtimes, - language defaults, and local source packaging metadata for deployments. - This is a public endpoint that doesn't require authentication. - responses: - '200': - description: Supported function runtimes + $ref: '#/components/schemas/Error' + '404': + description: Project, database, or restore not found content: application/json: schema: - $ref: '#/components/schemas/FunctionRuntimesResponse' - /functions/regions: - get: - tags: - - Functions - summary: List available regions for function deployment - operationId: listFunctionRegions - security: [] - description: | - Returns the configured regions where functions can be deployed, each annotated - with a human-readable label and country flag emoji for use in UI pickers. - This is a public endpoint that doesn't require authentication. - responses: - '200': - description: Available function deployment regions + $ref: '#/components/schemas/Error' + '503': + description: Backups are temporarily unavailable content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/FunctionRegion' - /projects/{id}/variables/{name}: - get: + $ref: '#/components/schemas/Error' + /projects/{id}/databases/{databaseName}/reset-password: + post: tags: - - Variables - summary: Get variable by name + - Databases + summary: Reset database password description: | - Returns a project-level environment variable used by deployed functions and frontends. - operationId: getVariable + Rotates the Volcano-managed PostgreSQL password used by clients when connecting + through pgproxy. This does not rotate or expose the internal owner password. + The returned password and connection string are the only client credentials that + will authenticate through pgproxy after reset. + + Existing connections are not interrupted; new ones must use the returned + string. Proxies pick the rotation up within a few seconds, so the previous + password can still open new connections until then. + operationId: resetDatabasePassword security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/VariableName' + - $ref: '#/components/parameters/DatabaseName' responses: '200': - description: Successful response + description: Password reset successful content: application/json: schema: - $ref: '#/components/schemas/Variable' + type: object + properties: + message: + type: string + role_name: + type: string + description: Volcano-managed per-database client login (also the pgproxy routing username) + example: volcano_client_11111111-1111-1111-1111-111111111111 + new_password: + type: string + description: New Volcano-managed client password. Always starts with `vpg_`. + connection_string: + type: string + description: Updated pgproxy connection string using Volcano-managed credentials. + '400': + description: Database is not active + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Variable not found + description: Database not found content: application/json: schema: $ref: '#/components/schemas/Error' - put: + '409': + description: | + A restore is running on the database. A restore replaces the + credentials as it finishes, so wait for it and rotate afterwards. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: | + Volcano could not check whether a restore is running, and will not + rotate a credential a restore might be about to replace. Retry. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/databases/{databaseName}/type: + patch: tags: - - Variables - summary: Update a variable + - Databases + summary: Update database size description: | - Updates a project-level environment variable and triggers asynchronous propagation - to deployed functions and frontends in the project's configured regions. - operationId: updateVariable + Change the size tier of a database. This may briefly interrupt active connections. + + **Available sizes:** + - `volcano-db-xs`: Up to ~1GB RAM - Development, small apps + - `volcano-db-s`: Up to ~4GB RAM - Production-ready, light traffic + - `volcano-db-m`: Up to ~8GB RAM - Medium traffic applications + - `volcano-db-l`: Up to ~16GB RAM - High traffic, larger datasets + - `volcano-db-xl`: Up to ~32GB RAM - Heavy workloads + - `volcano-db-2xl`: Up to ~64GB RAM - Enterprise-scale + operationId: updateDatabaseType security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/VariableName' + - $ref: '#/components/parameters/DatabaseName' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateVariableRequest' + $ref: '#/components/schemas/UpdateDatabaseTypeRequest' responses: '200': - description: Variable updated + description: Database type updated content: application/json: schema: - $ref: '#/components/schemas/Variable' + $ref: '#/components/schemas/Database' + '400': + description: Invalid database type '404': - description: Variable not found + description: Database not found + '409': + description: | + The database is not active — being provisioned, deleted, or + restored. Compute can only be changed while it is active. content: application/json: schema: $ref: '#/components/schemas/Error' '503': - description: Private variable membership writes are disabled during rollout + description: | + Volcano could not check whether a restore is running, and will not + reconfigure compute a restore might be moving. Retry. content: application/json: schema: $ref: '#/components/schemas/Error' - delete: + /projects/{id}/databases/{databaseName}/stats: + get: tags: - - Variables - summary: Delete a variable + - Databases + summary: Get database consumption metrics description: | - Deletes a project-level environment variable and triggers asynchronous propagation - of the removal to deployed functions and frontends in the project's configured regions. - operationId: deleteVariable + Retrieve consumption metrics including storage, compute time, and data transfer. + Metrics are aggregated at the project level. Defaults to last 24 hours. + + **Note:** Advanced metrics require an upgraded plan. + operationId: getDatabaseStats security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/VariableName' + - $ref: '#/components/parameters/DatabaseName' + - name: from + in: query + required: false + schema: + type: string + format: date-time + description: Start time in RFC3339 format (e.g., "2024-01-01T00:00:00Z"). Defaults to 24 hours ago. + - name: to + in: query + required: false + schema: + type: string + format: date-time + description: End time in RFC3339 format (e.g., "2024-01-02T00:00:00Z"). Defaults to now. + - name: granularity + in: query + required: false + schema: + type: string + enum: + - hourly + - daily + - monthly + default: hourly + description: Level of detail for metrics aggregation responses: - '204': - description: Variable deleted - '404': - description: Variable not found + '200': + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/Error' - /auth/password-policy: - get: - tags: - - Authentication - summary: Get the effective password policy - description: | - Returns the backend-enforced password bounds and compromised-password - screening status for the project identified by the anon key. A valid - anon key is required, but no route-specific auth permission is needed. - operationId: authGetPasswordPolicy - security: - - AnonKey: [] - responses: - '200': - description: Effective password policy + $ref: '#/components/schemas/DatabaseStats' + '400': + description: Invalid parameters content: application/json: schema: - $ref: '#/components/schemas/AuthPasswordPolicy' - '401': - description: Invalid, missing, or revoked anon key + $ref: '#/components/schemas/Error' + '404': + description: Database not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project or auth configuration not found + '503': + description: Database metrics not available or requires upgraded plan content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/signup: + /databases/{databaseName}/query/ping: post: tags: - - Authentication - summary: Sign up a new auth user + - Database Queries + summary: Database connectivity probe (REST API) description: | - Create a new end-user account. The project is determined from the anon key. - Requires project-specific anon key in Authorization header. - - **Session-less**: signup never issues a session. On success it returns a - uniform acknowledgement (`AuthSignupResponse`) with no tokens; the client - obtains a session with a subsequent `POST /auth/signin`. If email confirmation - is enabled for the project, a confirmation email is sent and - `confirmation_required` is `true`. + Connectivity probe that runs a fixed `SELECT 1` through pgproxy, using the + same authentication, status/bandwidth gating, and metering as the other + `/query/*` endpoints. - **Anti-enumeration**: a signup for an already-registered email returns the - exact same `201` response as a fresh signup — it never returns `409` — so the - response cannot be used to discover which emails are registered. - operationId: authSignup + Unlike those endpoints, ping takes **no request body** and performs **no + table-name validation**, so it works on any database — including a freshly + provisioned, empty one. It is a real committed round-trip through pgproxy, + so a `200` means the database is reachable and queryable. Used by the + dashboard's database connection test. + operationId: queryDatabasePing security: - - AnonKey: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - email - - password - properties: - email: - type: string - format: email - password: - type: string - description: | - Password validated after NFC normalization against the - policy returned by GET /auth/password-policy. - user_metadata: - type: object - additionalProperties: true + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DatabaseName' responses: - '201': - description: | - Signup acknowledged (session-less). Returned identically for a new - account and for an already-registered email (anti-enumeration). + '200': + description: Database is reachable + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - $ref: '#/components/schemas/AuthSignupResponse' - '400': - description: Invalid input (bad email/password format) + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - '?column?': 1 + count: 1 '401': - description: | - Unauthorized - Invalid, tampered, revoked, or wrong-project anon key + description: Not authenticated + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '403': - description: | - Forbidden - Signups disabled, anon key lacks signup permission, or the - email domain is not in `allowed_email_domains`. The internal - `anonymous.volcano.internal` domain is reserved for anonymous - accounts and is refused whatever the project allows. - '429': - description: Rate limit exceeded - '503': - description: Compromised-password screening is temporarily unavailable + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/signin: + '404': + description: Database not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/DatabaseQueryCapExceeded' + /databases/{databaseName}/query/select: post: tags: - - Authentication - summary: Sign in an auth user + - Database Queries + summary: Query database with SELECT (REST API) description: | - Authenticate with email and password. Requires an anon key. + Query your database using a simple REST API - no SQL required! - Set `session_mode` to `cookie` to request HttpOnly refresh-token - storage. Cookie mode is honored only for an exact, credentialed CORS - origin on the same schemeful site as this API. Otherwise the response - retains the refresh token in its body. - operationId: authSignin + **Authentication:** Requires auth user access token (from signup/signin) + + **Row-Level Security:** Automatically enforced - you see only data you have access to + + **Use Cases:** + - Query from browser/mobile apps + - Simple data retrieval + - Filtered searches with sorting and pagination + + **Note:** For complex queries (JOINs, CTEs), use functions with direct SQL + operationId: queryDatabaseSelect security: - - AnonKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DatabaseName' requestBody: required: true content: application/json: schema: - type: object - required: - - email - - password - properties: - email: - type: string - password: - type: string - session_mode: - type: string - enum: - - cookie + $ref: '#/components/schemas/DatabaseSelectRequest' responses: '200': - description: Signin successful + description: Query successful + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - $ref: '#/components/schemas/AuthTokenResponse' + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - id: uuid-123 + title: My Post + content: Post content + status: published + views: 150 + created_at: '2026-01-13T10:00:00Z' + count: 1 '400': - description: Invalid input (missing email/password) + description: Invalid query + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': - description: | - Unauthorized - Invalid credentials, invalid/tampered/revoked anon key, - or account banned/deleted + description: Not authenticated + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '403': - description: | - Forbidden - Anon key lacks signin permission, or the email domain is - not in `allowed_email_domains` while `allowed_email_domains_mode` is - `signup_and_signin`. The domain is taken from the account's canonical - email (its primary identity), which is not necessarily the address in - the request. + description: Access denied + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Database not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '429': - description: Rate limit exceeded - /auth/refresh: + $ref: '#/components/responses/DatabaseQueryCapExceeded' + /databases/{databaseName}/query/insert: post: tags: - - Authentication - summary: Refresh access token + - Database Queries + summary: Insert data into database (REST API) description: | - Get a new access token using a refresh token. Requires an anon key. + Insert new rows into your database using REST API. - Send `refresh_token` in the body for the default flow. An eligible - cookie-mode browser request may instead send `session_mode: cookie` - with an empty token or omit the request body; the API reads and resets - the project's HttpOnly cookie and omits `refresh_token` from the - response. - operationId: authRefresh + **Authentication:** Requires auth user access token + + **Auto-set user_id:** If your table has a trigger using `auth.uid()`, + user_id will be automatically set to the authenticated user + + **Security:** Row-Level Security policies are enforced + operationId: queryDatabaseInsert security: - - AnonKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DatabaseName' requestBody: - required: false + required: true content: application/json: schema: - type: object - properties: - refresh_token: - type: string - session_mode: - type: string - enum: - - cookie + $ref: '#/components/schemas/DatabaseInsertRequest' responses: '200': - description: Token refreshed + description: Insert successful + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - $ref: '#/components/schemas/AuthTokenResponse' + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - id: uuid-123 + title: My New Post + content: This is the content + status: draft + user_id: user-uuid + created_at: '2026-01-13T10:00:00Z' + count: 1 + '400': + description: Invalid request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': - description: Invalid or expired refresh token + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: | - The account's email domain is not in `allowed_email_domains` while - `allowed_email_domains_mode` is `signup_and_signin`, so the session - cannot be extended + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - description: Rate limit exceeded + '404': + description: Database not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/logout: + '429': + $ref: '#/components/responses/DatabaseQueryCapExceeded' + /databases/{databaseName}/query/update: post: tags: - - Authentication - summary: Logout (revoke refresh token) + - Database Queries + summary: Update data in database (REST API) description: | - Invalidate a refresh token. Requires an anon key. + Update existing rows in your database using REST API. - Send `refresh_token` for the default flow. An eligible cookie-mode - browser request may instead send `session_mode: cookie` with an empty - token; logout remains idempotent when the cookie is missing or expired. - operationId: authLogout - security: - - AnonKey: [] - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - refresh_token: - type: string - session_mode: - type: string - enum: - - cookie - responses: - '204': - description: Logged out successfully - /auth/forgot-password: - post: - tags: - - Authentication - summary: Request password reset - description: | - Generates recovery token and stores it (email sending pending). - Returns generic message to prevent email enumeration. - Project is identified via the anon key. - operationId: authForgotPassword + **Security:** Row-Level Security ensures you can only update data you have access to + + **Safety:** Requires at least one filter to prevent accidental mass updates. A + request with no `filters` is rejected with `400` (mirrors delete). This matters + for service-key queries, which run with full access and bypass RLS. + + **Note:** If RLS blocks the update, an empty result is returned (not an error) + operationId: queryDatabaseUpdate security: - - AnonKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DatabaseName' requestBody: required: true content: application/json: schema: - type: object - required: - - email - properties: - email: - type: string - format: email + $ref: '#/components/schemas/DatabaseUpdateRequest' responses: '200': - description: Generic success message (doesn't reveal if email exists) + description: Update successful + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - type: object - properties: - message: - type: string - example: If the email exists, a password reset link has been sent + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - id: post-uuid + title: Updated Title + status: published + updated_at: '2026-01-13T10:05:00Z' + count: 1 + '400': + description: Invalid request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Not authenticated + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '403': - description: Password reset is disabled for this project + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - description: Rate limit exceeded (10 requests per hour per IP) + '404': + description: Database not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/reset-password: + '429': + $ref: '#/components/responses/DatabaseQueryCapExceeded' + /databases/{databaseName}/query/delete: post: tags: - - Authentication - summary: Reset password with recovery token + - Database Queries + summary: Delete data from database (REST API) description: | - Reset password using recovery token from forgot-password. - Revokes all existing sessions for security. - operationId: authResetPassword + Delete rows from your database using REST API. + + **Safety:** Requires at least one filter to prevent accidental mass deletions + + **Security:** Row-Level Security ensures you can only delete data you have access to + + **Note:** If RLS blocks the delete, an empty result is returned (not an error) + operationId: queryDatabaseDelete security: - - AnonKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DatabaseName' requestBody: required: true content: application/json: schema: - type: object - required: - - token - - new_password - properties: - token: - type: string - description: Recovery token from forgot-password - new_password: - type: string - description: | - Password validated after NFC normalization against the - policy returned by GET /auth/password-policy. + $ref: '#/components/schemas/DatabaseDeleteRequest' responses: '200': - description: Password reset successful + description: Delete successful + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - type: object - properties: - message: - type: string + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - id: post-uuid + title: Deleted Post + count: 1 '400': - description: Password doesn't meet requirements + description: Invalid request or missing filters + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': - description: Invalid or expired token - '503': - description: Compromised-password screening is temporarily unavailable + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/confirm: - post: - tags: - - Authentication - summary: Confirm email address - description: | - Confirm email address using token sent via email. - Required if require_email_confirmation is enabled. - operationId: authConfirmEmail - security: - - AnonKey: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - token - properties: - token: - type: string - description: Confirmation token from email - responses: - '200': - description: Email confirmed or already confirmed + '403': + description: Access denied content: application/json: schema: - type: object - properties: - message: - type: string - enum: - - Email confirmed successfully - - Email already confirmed - examples: - confirmed: - summary: Fresh confirmation - value: - message: Email confirmed successfully - alreadyConfirmed: - summary: Token belongs to already-confirmed user - value: - message: Email already confirmed - '400': - description: Missing confirmation token in request body - '401': - description: Invalid or expired token - /auth/resend-confirmation: + $ref: '#/components/schemas/Error' + '404': + description: Database not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/DatabaseQueryCapExceeded' + /databases/{databaseName}/branches/{branchName}/query/ping: post: tags: - - Authentication - summary: Resend confirmation email + - Database Queries + summary: Database connectivity probe (REST API) description: | - Resend email confirmation link. - Returns generic message to prevent email enumeration. - No email is sent when the account does not exist or is already confirmed. - If the account exists and is unconfirmed, a new token is generated and - any previous confirmation token is invalidated. - operationId: authResendConfirmation + Connectivity probe that runs a fixed `SELECT 1` through pgproxy, using the + same authentication, status/bandwidth gating, and metering as the other + `/query/*` endpoints. + + Unlike those endpoints, ping takes **no request body** and performs **no + table-name validation**, so it works on any database — including a freshly + provisioned, empty one. It is a real committed round-trip through pgproxy, + so a `200` means the database is reachable and queryable. Used by the + dashboard's database connection test. + + **Branch-targeted.** Runs against the named branch instead of the parent + database, using the branch's own credentials. The branch must be `active` + and unexpired. Nothing about this request can reach the parent's data. + operationId: queryDatabaseBranchPing security: - - AnonKey: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - email - properties: - email: - type: string - format: email + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/BranchName' responses: '200': - description: Generic success message + description: Database is reachable + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - type: object - properties: - message: - type: string - '429': - description: Rate limit exceeded + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - '?column?': 1 + count: 1 + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/signup-anonymous: - post: - tags: - - Authentication - summary: Create anonymous user - description: | - Create guest user without email/password. - - User metadata (like display_name) can be included and will appear in realtime presence events. - Requires enable_anonymous_signins to be true. - operationId: authSignupAnonymous - security: - - AnonKey: [] - requestBody: - description: Optional user metadata - content: - application/json: - schema: - type: object - properties: - user_metadata: - type: object - additionalProperties: true - description: Custom user metadata (e.g., display_name, avatar_url) - example: - display_name: Alice - avatar_url: https://example.com/alice.jpg - responses: - '201': - description: Anonymous user created - content: - application/json: - schema: - $ref: '#/components/schemas/AuthTokenResponse' '403': - description: Anonymous signins disabled - /auth/user/convert-anonymous: - post: - tags: - - Authentication - summary: Convert anonymous user to authenticated - description: | - Add email and password to anonymous user. - Requires auth user access token. - If require_email_confirmation is enabled for the project, the converted - user remains unconfirmed until /auth/confirm succeeds. When email - sending is enabled, a confirmation email is sent during conversion. - operationId: authConvertAnonymous - security: - - AuthUserAccessToken: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - email - - password - properties: - email: - type: string - format: email - password: - type: string - description: | - Password validated after NFC normalization against the - policy returned by GET /auth/password-policy. - user_metadata: - type: object - additionalProperties: true - responses: - '200': - description: User converted successfully + description: Access denied content: application/json: schema: - type: object - properties: - user: - $ref: '#/components/schemas/AuthUser' - '400': - description: Not an anonymous user - '403': - description: | - The chosen email domain is not in `allowed_email_domains` - '409': - description: Email already in use - '503': - description: Compromised-password screening is temporarily unavailable + $ref: '#/components/schemas/Error' + '404': + description: Database or branch not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/user/change-email: + '429': + $ref: '#/components/responses/DatabaseQueryCapExceeded' + '503': + $ref: '#/components/responses/DatabaseBranchQueryUnavailable' + /databases/{databaseName}/branches/{branchName}/query/select: post: tags: - - Authentication - summary: Request email change + - Database Queries + summary: Query database with SELECT (REST API) description: | - Request to change user's email address. - Sends confirmation token to new email address. - operationId: authRequestEmailChange + Query your database using a simple REST API - no SQL required! + + **Authentication:** Requires auth user access token (from signup/signin) + + **Row-Level Security:** Automatically enforced - you see only data you have access to + + **Use Cases:** + - Query from browser/mobile apps + - Simple data retrieval + - Filtered searches with sorting and pagination + + **Note:** For complex queries (JOINs, CTEs), use Lambda functions with direct SQL + + **Branch-targeted.** Runs against the named branch instead of the parent + database, using the branch's own credentials. The branch must be `active` + and unexpired. Nothing about this request can reach the parent's data. + operationId: queryDatabaseBranchSelect security: - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/BranchName' requestBody: required: true content: application/json: schema: - type: object - required: - - new_email - properties: - new_email: - type: string - format: email + $ref: '#/components/schemas/DatabaseSelectRequest' responses: '200': - description: Confirmation email sent + description: Query successful + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - type: object - properties: - message: - type: string - new_email: - type: string + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - id: uuid-123 + title: My Post + content: Post content + status: published + views: 150 + created_at: '2026-01-13T10:00:00Z' + count: 1 '400': - description: Invalid email format or same as current email + description: Invalid query content: application/json: schema: @@ -7686,58 +7853,76 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: | - The requested email domain is not in `allowed_email_domains` + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: Email already in use + '404': + description: Database or branch not found content: application/json: schema: $ref: '#/components/schemas/Error' '429': - description: Rate limit exceeded (10 requests per hour per IP) - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /auth/user/confirm-email-change: + $ref: '#/components/responses/DatabaseQueryCapExceeded' + '503': + $ref: '#/components/responses/DatabaseBranchQueryUnavailable' + /databases/{databaseName}/branches/{branchName}/query/insert: post: tags: - - Authentication - summary: Confirm email change - description: Confirm email change with token sent to new address - operationId: authConfirmEmailChange + - Database Queries + summary: Insert data into database (REST API) + description: | + Insert new rows into your database using REST API. + + **Authentication:** Requires auth user access token + + **Auto-set user_id:** If your table has a trigger using `auth.uid()`, + user_id will be automatically set to the authenticated user + + **Security:** Row-Level Security policies are enforced + + **Branch-targeted.** Runs against the named branch instead of the parent + database, using the branch's own credentials. The branch must be `active` + and unexpired. Nothing about this request can reach the parent's data. + operationId: queryDatabaseBranchInsert security: - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/BranchName' requestBody: required: true content: application/json: schema: - type: object - required: - - email_change_token - properties: - email_change_token: - type: string + $ref: '#/components/schemas/DatabaseInsertRequest' responses: '200': - description: Email changed successfully + description: Insert successful + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - type: object - properties: - message: - type: string - user: - $ref: '#/components/schemas/AuthUser' + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - id: uuid-123 + title: My New Post + content: This is the content + status: draft + user_id: user-uuid + created_at: '2026-01-13T10:00:00Z' + count: 1 '400': - description: Invalid or expired token, or no pending email change + description: Invalid request content: application/json: schema: @@ -7749,181 +7934,75 @@ paths: schema: $ref: '#/components/schemas/Error' '403': - description: | - The pending email domain is no longer in `allowed_email_domains`. - Re-checked here because the allowlist can narrow between the request - and the confirmation. + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: Email is now in use by another user + '404': + description: Database or branch not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/user/cancel-email-change: - delete: + '429': + $ref: '#/components/responses/DatabaseQueryCapExceeded' + '503': + $ref: '#/components/responses/DatabaseBranchQueryUnavailable' + /databases/{databaseName}/branches/{branchName}/query/update: + post: tags: - - Authentication - summary: Cancel pending email change - operationId: authCancelEmailChange - security: - - AuthUserAccessToken: [] - responses: - '200': - description: Email change cancelled - content: - application/json: - schema: - type: object - properties: - message: - type: string - '401': - description: Not authenticated - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /auth/user/sessions: - get: - tags: - - Authentication - summary: Get current user's sessions + - Database Queries + summary: Update data in database (REST API) description: | - Returns paginated sessions for the currently authenticated user. - Each session includes device info, IP addresses, and activity timestamps. - The current session is marked with `is_current: true`. + Update existing rows in your database using REST API. - **Ordering and pagination.** Without `sort`, results are ordered by most - recent activity and paged with `page`/`limit`, returning the - `sessions`/`total`/`page`/`limit`/`total_pages` body below. This is the - legacy default and is preserved for existing clients. + **Security:** Row-Level Security ensures you can only update data you have access to - Send `sort=created_at` to opt into the standard list contract: results are - ordered by session start (newest first) and may be paged either with - `page`/`limit` or by cursor with `cursor`/`ending_before` plus a bounded - `offset` past the cursor anchor. Cursor responses use the shared - `data` envelope with `next_cursor`/`prev_cursor`. + **Safety:** Requires at least one filter to prevent accidental mass updates. A + request with no `filters` is rejected with `400` (mirrors delete). This matters + for service-key queries, which run with full access and bypass RLS. - Unlike other list endpoints, sending `limit` without `page` does **not** - select cursor mode here; `sort=created_at` is the only opt-in. Cursor - pagination is rejected with 400 for the activity order, because - `last_activity_at` changes whenever a session refreshes its token: a row - that crosses the cursor anchor between two requests would be skipped and - never shown. The `status=expired` filter is also offset-only because a - session can expire above the cursor anchor during a walk. Sending that - filter in cursor mode, `page` with `cursor` or `ending_before`, or both - cursor directions returns 400. - operationId: authGetMySessions + **Note:** If RLS blocks the update, an empty result is returned (not an error) + + **Branch-targeted.** Runs against the named branch instead of the parent + database, using the branch's own credentials. The branch must be `active` + and unexpired. Nothing about this request can reach the parent's data. + operationId: queryDatabaseBranchUpdate security: - AuthUserAccessToken: [] parameters: - - name: page - in: query - description: Page number (1-indexed) - schema: - type: integer - minimum: 1 - default: 1 - - name: limit - in: query - description: Number of sessions per page (max 100) - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: sort - in: query - description: | - Sort key. `last_activity` (default) orders by most recent activity and - supports offset pagination only. `created_at` orders by session start - and supports both offset and cursor pagination. - schema: - type: string - enum: - - last_activity - - created_at - default: last_activity - - name: status - in: query - description: | - Filter by whether the session can still be refreshed. Omit for every - stored session, including expired ones. `expired` is not supported - with cursor pagination. - schema: - type: string - enum: - - active - - expired - - name: cursor - in: query - description: | - Opaque keyset cursor from a previous response's `next_cursor`. Requires - `sort=created_at`; mutually exclusive with `page` and `ending_before`. - schema: - type: string - - name: ending_before - in: query - description: | - Opaque keyset cursor from a previous response's `prev_cursor`, paging - backward. Requires `sort=created_at`; mutually exclusive with `page` - and `cursor`. - schema: - type: string - - name: offset - in: query - description: | - Bounded number of rows to skip past the cursor anchor (the hybrid - jump, maximum 100000). Ignored unless `cursor` or `ending_before` is - supplied. - schema: - type: integer - minimum: 0 - maximum: 100000 + - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/BranchName' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DatabaseUpdateRequest' responses: '200': - description: Paginated list of user sessions + description: Update successful + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' content: application/json: schema: - type: object - properties: - sessions: - type: array - items: - $ref: '#/components/schemas/AuthSession' - total: - type: integer - description: Total number of sessions - page: - type: integer - description: Current page number - limit: - type: integer - description: Number of sessions per page - total_pages: - type: integer - description: Total number of pages - data: - type: array - description: Sessions for this page (cursor pagination only) - items: - $ref: '#/components/schemas/AuthSession' - has_more: - type: boolean - description: Whether a further page exists (cursor pagination only) - next_cursor: - type: string - description: Opaque cursor for the next page (cursor pagination only) - prev_cursor: - type: string - description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - id: post-uuid + title: Updated Title + status: published + updated_at: '2026-01-13T10:05:00Z' + count: 1 '400': - description: Invalid or conflicting pagination parameters + description: Invalid request content: application/json: schema: @@ -7934,1317 +8013,1456 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - delete: - tags: - - Authentication - summary: Sign out from all other devices - description: | - Deletes all sessions except the current one. - Use this to log out from all other devices while keeping the current session active. - operationId: authDeleteAllMySessions - security: - - AuthUserAccessToken: [] - responses: - '204': - description: All other sessions deleted - '401': - description: Not authenticated + '403': + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/user/sessions/{sessionId}: - delete: + '404': + description: Database or branch not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/DatabaseQueryCapExceeded' + '503': + $ref: '#/components/responses/DatabaseBranchQueryUnavailable' + /databases/{databaseName}/branches/{branchName}/query/delete: + post: tags: - - Authentication - summary: Sign out from specific device + - Database Queries + summary: Delete data from database (REST API) description: | - Deletes a specific session, logging out that device. - You can get session IDs from the list sessions endpoint. - operationId: authDeleteMySession + Delete rows from your database using REST API. + + **Safety:** Requires at least one filter to prevent accidental mass deletions + + **Security:** Row-Level Security ensures you can only delete data you have access to + + **Note:** If RLS blocks the delete, an empty result is returned (not an error) + + **Branch-targeted.** Runs against the named branch instead of the parent + database, using the branch's own credentials. The branch must be `active` + and unexpired. Nothing about this request can reach the parent's data. + operationId: queryDatabaseBranchDelete security: - AuthUserAccessToken: [] parameters: - - name: sessionId - in: path - required: true - description: The session ID to delete - schema: - type: string - format: uuid + - $ref: '#/components/parameters/DatabaseName' + - $ref: '#/components/parameters/BranchName' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/DatabaseDeleteRequest' responses: - '204': - description: Session deleted + '200': + description: Delete successful + headers: + X-Volcano-Proxy-Ms: + $ref: '#/components/headers/DatabaseQueryProxyMs' + X-Volcano-Proxy-Handler-Ms: + $ref: '#/components/headers/DatabaseQueryProxyHandlerMs' + X-Volcano-Compute-Ms: + $ref: '#/components/headers/DatabaseQueryComputeMs' + content: + application/json: + schema: + $ref: '#/components/schemas/DatabaseQueryResult' + example: + data: + - id: post-uuid + title: Deleted Post + count: 1 + '400': + description: Invalid request or missing filters + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' + '403': + description: Access denied + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Session not found + description: Database or branch not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/user: - get: - tags: - - Authentication - summary: Get current user profile - description: Returns authenticated user's profile. Requires access token. - operationId: authGetUser - security: - - AuthUserAccessToken: [] + '429': + $ref: '#/components/responses/DatabaseQueryCapExceeded' + '503': + $ref: '#/components/responses/DatabaseBranchQueryUnavailable' + /databases/regions: + get: + tags: + - Databases + summary: List platform-supported regions for database provisioning + operationId: listDatabaseRegions + description: | + Returns the regions enabled for database provisioning in this platform environment. + These are the same regions offered for function deployment, and the only values + the `region` field of a database accepts. + This is a public endpoint that doesn't require authentication. responses: '200': - description: User profile - content: - application/json: - schema: - type: object - properties: - user: - $ref: '#/components/schemas/AuthUser' - '401': - description: Not authenticated - access token missing or invalid + description: List of platform-supported regions content: application/json: schema: - $ref: '#/components/schemas/Error' - put: - tags: - - Authentication - summary: Update user profile - description: Update password or metadata. Requires access token. - operationId: authUpdateUser - security: - - AuthUserAccessToken: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - password: - type: string - description: | - Password validated after NFC normalization against the - policy returned by GET /auth/password-policy. - user_metadata: + type: array + items: type: object - additionalProperties: true - description: | - Metadata keys to merge into the current user metadata. - Omitted keys remain unchanged; set a key to null to remove it. - Merging is shallow; nested objects replace the stored value for that top-level key. + properties: + id: + type: string + example: aws-us-east-1 + description: Region identifier for API usage + name: + type: string + example: US East (N. Virginia) + description: Human-readable region location + /databases/postgres-versions: + get: + tags: + - Databases + summary: List available PostgreSQL versions + operationId: listPostgresVersions + description: | + Returns a list of supported PostgreSQL major versions for database provisioning. + This is a public endpoint that doesn't require authentication. responses: '200': - description: Profile updated - content: - application/json: - schema: - type: object - properties: - user: - $ref: '#/components/schemas/AuthUser' - '400': - description: Bad request - invalid password or metadata format + description: List of available PostgreSQL versions content: application/json: schema: - $ref: '#/components/schemas/Error' - '401': - description: Not authenticated - access token missing or invalid + type: array + items: + type: object + properties: + version: + type: string + example: '16' + description: PostgreSQL major version number + name: + type: string + example: PostgreSQL 16 + description: Human-readable version name + default: + type: boolean + description: Whether this is the default version (recommended) + deprecated: + type: boolean + description: Whether this version is deprecated (approaching EOL) + /functions/runtimes: + get: + tags: + - Functions + summary: List supported function runtimes + operationId: listFunctionRuntimes + security: [] + description: | + Returns the public function runtime catalog used by CLI clients to select supported runtimes, + language defaults, and local source packaging metadata for deployments. + This is a public endpoint that doesn't require authentication. + responses: + '200': + description: Supported function runtimes content: application/json: schema: - $ref: '#/components/schemas/Error' - '503': - description: Compromised-password screening is temporarily unavailable + $ref: '#/components/schemas/FunctionRuntimesResponse' + /functions/regions: + get: + tags: + - Functions + summary: List available regions for function deployment + operationId: listFunctionRegions + security: [] + description: | + Returns the configured regions where functions can be deployed, each annotated + with a human-readable label and country flag emoji for use in UI pickers. + This is a public endpoint that doesn't require authentication. + responses: + '200': + description: Available function deployment regions content: application/json: schema: - $ref: '#/components/schemas/Error' - /auth/user/identities: + type: array + items: + $ref: '#/components/schemas/FunctionRegion' + /projects/{id}/variables/{name}: get: tags: - - Authentication - summary: List the current user's identities + - Variables + summary: Get variable by name description: | - Returns every real email identity the account owns. An account can own - multiple identities (for example a password identity plus one or more - OAuth identities on different emails). Anonymous accounts have no real - identity and return an empty list. - operationId: authListIdentities + Returns a project-level environment variable used by deployed functions and frontends. + operationId: getVariable security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/VariableName' responses: '200': - description: List of identities + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/AuthIdentitiesResponse' - '401': - description: Not authenticated + $ref: '#/components/schemas/Variable' + '404': + description: Variable not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/user/identities/{identityId}: - delete: + put: tags: - - Authentication - summary: Unlink an identity from the current user + - Variables + summary: Update a variable description: | - Removes a non-primary identity and its attached sign-in methods. Refused - when the identity is the account's primary, its only identity, or when - removing it would leave the account with no way to sign in. - operationId: authUnlinkIdentity + Updates a project-level environment variable and triggers asynchronous propagation + to deployed functions and frontends in the project's configured regions. + operationId: updateVariable security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: - - name: identityId - in: path - required: true - description: The identity ID to unlink - schema: - type: string - format: uuid + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/VariableName' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateVariableRequest' responses: - '204': - description: Identity unlinked - '400': - description: Identity cannot be unlinked (primary, last, or would remove last sign-in method), or the identity id is malformed + '200': + description: Variable updated + content: + application/json: + schema: + $ref: '#/components/schemas/Variable' + '404': + description: Variable not found content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Not authenticated + '503': + description: Private variable membership writes are disabled during rollout content: application/json: schema: $ref: '#/components/schemas/Error' + delete: + tags: + - Variables + summary: Delete a variable + description: | + Deletes a project-level environment variable and triggers asynchronous propagation + of the removal to deployed functions and frontends in the project's configured regions. + operationId: deleteVariable + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/VariableName' + responses: + '204': + description: Variable deleted '404': - description: Identity not found + description: Variable not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/user/methods: + /auth/password-policy: get: tags: - Authentication - summary: List the current user's sign-in methods + summary: Get the effective password policy description: | - Returns a flat list of every sign-in method the account owns (password, - each OAuth provider, and any active anonymous method), with the primary - method flagged. Password stubs and converted anonymous methods are excluded. - operationId: authListMethods + Returns the backend-enforced password bounds and compromised-password + screening status for the project identified by the anon key. A valid + anon key is required, but no route-specific auth permission is needed. + operationId: authGetPasswordPolicy security: - - AuthUserAccessToken: [] + - AnonKey: [] responses: '200': - description: List of sign-in methods + description: Effective password policy content: application/json: schema: - $ref: '#/components/schemas/AuthMethodsResponse' + $ref: '#/components/schemas/AuthPasswordPolicy' '401': - description: Not authenticated + description: Invalid, missing, or revoked anon key content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/user/methods/{methodId}/promote: + '404': + description: Project or auth configuration not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /auth/signup: post: tags: - Authentication - summary: Set a method as the account's primary + summary: Sign up a new auth user description: | - Promotes the given method to the account's primary sign-in method. The - account's canonical email is re-derived from the promoted method's identity. - Refused for password stubs and converted anonymous methods, and for an - identity whose domain is outside the project's `allowed_email_domains`. - operationId: authPromoteMethod + Create a new end-user account. The project is determined from the anon key. + Requires project-specific anon key in Authorization header. + + **Session-less**: signup never issues a session. On success it returns a + uniform acknowledgement (`AuthSignupResponse`) with no tokens; the client + obtains a session with a subsequent `POST /auth/signin`. If email confirmation + is enabled for the project, a confirmation email is sent and + `confirmation_required` is `true`. + + **Anti-enumeration**: a signup for an already-registered email returns the + exact same `201` response as a fresh signup — it never returns `409` — so the + response cannot be used to discover which emails are registered. + operationId: authSignup security: - - AuthUserAccessToken: [] - parameters: - - name: methodId - in: path - required: true - description: The method ID to promote - schema: - type: string - format: uuid + - AnonKey: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - email + - password + properties: + email: + type: string + format: email + password: + type: string + description: | + Password validated after NFC normalization against the + policy returned by GET /auth/password-policy. + user_metadata: + type: object + additionalProperties: true responses: - '200': - description: The promoted method + '201': + description: | + Signup acknowledged (session-less). Returned identically for a new + account and for an already-registered email (anti-enumeration). content: application/json: schema: - $ref: '#/components/schemas/AuthMethodSummary' + $ref: '#/components/schemas/AuthSignupResponse' '400': - description: This method cannot be set as primary (password stub, converted anonymous method, or unverified email), or the method id is malformed - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + description: Invalid input (bad email/password format) '401': - description: Not authenticated - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + description: | + Unauthorized - Invalid, tampered, revoked, or wrong-project anon key '403': - description: The promoted identity's email domain is not in the project's allowed_email_domains - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '404': - description: Method not found + description: | + Forbidden - Signups disabled, anon key lacks signup permission, or the + email domain is not in `allowed_email_domains`. The internal + `anonymous.volcano.internal` domain is reserved for anonymous + accounts and is refused whatever the project allows. + '429': + description: Rate limit exceeded + '503': + description: Compromised-password screening is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/auth/insights: - get: + /auth/signin: + post: tags: - - Auth Admin - summary: Get auth user insights + - Authentication + summary: Sign in an auth user description: | - Returns current auth-user totals, rolling 30-day active users, and - zero-filled signup and successful sign-in counts for an inclusive UTC - date range. Weeks start on Monday. Sign-in counts and active-user - activity begin when collection is deployed. Historical signup counts - are backfilled from users present at deployment. Token refreshes affect - active users but not the sign-in series. - operationId: getAuthInsights + Authenticate with email and password. Requires an anon key. + + Set `session_mode` to `cookie` to request HttpOnly refresh-token + storage. Cookie mode is honored only for an exact, credentialed CORS + origin on the same schemeful site as this API. Otherwise the response + retains the refresh token in its body. A frontend on its default + Volcano URL is cross-site with this API and so always gets the body + token. + operationId: authSignin security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: from - in: query - description: Inclusive UTC start date. Defaults to 29 days before `to`. - schema: - type: string - format: date - - name: to - in: query - description: Inclusive UTC end date. Defaults to today. - schema: - type: string - format: date - - name: interval - in: query - description: Chart bucket size. Defaults to `day`. - schema: - $ref: '#/components/schemas/AuthInsightsInterval' + - AnonKey: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - email + - password + properties: + email: + type: string + password: + type: string + session_mode: + type: string + enum: + - cookie responses: '200': - description: Auth insights retrieved + description: Signin successful content: application/json: schema: - $ref: '#/components/schemas/AuthInsightsResponse' - example: - project_id: 4f165080-a931-4e03-b3bd-41c45c3f0058 - observed_at: '2026-07-20T18:00:00Z' - window: - from: '2026-06-21' - to: '2026-07-20' - interval: day - summary: - total_users: 1234 - active_users_30d: 418 - series: - - bucket_start: '2026-07-20' - signups: 12 - signins: 97 - is_partial: true + $ref: '#/components/schemas/AuthTokenResponse' '400': - description: Invalid date range or interval + description: Invalid input (missing email/password) + '401': + description: | + Unauthorized - Invalid credentials, invalid/tampered/revoked anon key, + or account banned/deleted + '403': + description: | + Forbidden - Anon key lacks signin permission, or the email domain is + not in `allowed_email_domains` while `allowed_email_domains_mode` is + `signup_and_signin`. The domain is taken from the account's canonical + email (its primary identity), which is not necessarily the address in + the request. + '429': + description: Rate limit exceeded + /auth/refresh: + post: + tags: + - Authentication + summary: Refresh access token + description: | + Get a new access token using a refresh token. Requires an anon key. + + Send `refresh_token` in the body for the default flow. An eligible + cookie-mode browser request may instead send `session_mode: cookie` + with an empty token or omit the request body; the API reads and resets + the project's HttpOnly cookie and omits `refresh_token` from the + response. + operationId: authRefresh + security: + - AnonKey: [] + requestBody: + required: false + content: + application/json: + schema: + type: object + properties: + refresh_token: + type: string + session_mode: + type: string + enum: + - cookie + responses: + '200': + description: Token refreshed content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/AuthTokenResponse' '401': - description: Unauthorized - invalid or missing token + description: Invalid or expired refresh token content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: Access denied - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '404': - description: Project not found + description: | + The account's email domain is not in `allowed_email_domains` while + `allowed_email_domains_mode` is `signup_and_signin`, so the session + cannot be extended content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '429': + description: Rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/auth/users: - get: + /auth/logout: + post: tags: - - Auth Admin - summary: List all auth users (admin) - description: List auth users in project. Requires platform token. - operationId: listAuthUsers - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' - - name: status - in: query - required: false - description: Filter by effective status. `banned` returns only currently-banned users; an expired temporary ban lists as `active`. - schema: - type: string - enum: - - active - - banned + - Authentication + summary: Logout (revoke refresh token) + description: | + Invalidate a refresh token. Requires an anon key. + + Send `refresh_token` for the default flow. An eligible cookie-mode + browser request may instead send `session_mode: cookie` with an empty + token; logout remains idempotent when the cookie is missing or expired. + operationId: authLogout + security: + - AnonKey: [] + requestBody: + required: false + content: + application/json: + schema: + type: object + properties: + refresh_token: + type: string + session_mode: + type: string + enum: + - cookie + responses: + '204': + description: Logged out successfully + /auth/forgot-password: + post: + tags: + - Authentication + summary: Request password reset + description: | + Generates recovery token and stores it (email sending pending). + Returns generic message to prevent email enumeration. + Project is identified via the anon key. + operationId: authForgotPassword + security: + - AnonKey: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - email + properties: + email: + type: string + format: email responses: '200': - description: Successful response + description: Generic success message (doesn't reveal if email exists) content: application/json: schema: - $ref: '#/components/schemas/PaginatedAuthUsers' + type: object + properties: + message: + type: string + example: If the email exists, a password reset link has been sent + '403': + description: Password reset is disabled for this project + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: Rate limit exceeded (10 requests per hour per IP) + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /auth/reset-password: + post: + tags: + - Authentication + summary: Reset password with recovery token + description: | + Reset password using recovery token from forgot-password. + Revokes all existing sessions for security. + operationId: authResetPassword + security: + - AnonKey: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - token + - new_password + properties: + token: + type: string + description: Recovery token from forgot-password + new_password: + type: string + description: | + Password validated after NFC normalization against the + policy returned by GET /auth/password-policy. + responses: + '200': + description: Password reset successful + content: + application/json: + schema: + type: object + properties: + message: + type: string '400': - description: Invalid status or pagination parameters + description: Password doesn't meet requirements + '401': + description: Invalid or expired token + '503': + description: Compromised-password screening is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/auth/users/{userId}: - get: + /auth/confirm: + post: tags: - - Auth Admin - summary: Get specific auth user (admin) - operationId: getAuthUser + - Authentication + summary: Confirm email address + description: | + Confirm email address using token sent via email. + Required if require_email_confirmation is enabled. + operationId: authConfirmEmail security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: userId - in: path - required: true - schema: - type: string - format: uuid + - AnonKey: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - token + properties: + token: + type: string + description: Confirmation token from email responses: '200': - description: Auth user details + description: Email confirmed or already confirmed content: application/json: schema: - $ref: '#/components/schemas/AuthUser' - delete: + type: object + properties: + message: + type: string + enum: + - Email confirmed successfully + - Email already confirmed + examples: + confirmed: + summary: Fresh confirmation + value: + message: Email confirmed successfully + alreadyConfirmed: + summary: Token belongs to already-confirmed user + value: + message: Email already confirmed + '400': + description: Missing confirmation token in request body + '401': + description: Invalid or expired token + /auth/resend-confirmation: + post: tags: - - Auth Admin - summary: Delete auth user (admin) - description: Soft-deletes user and revokes all sessions - operationId: deleteAuthUser + - Authentication + summary: Resend confirmation email + description: | + Resend email confirmation link. + Returns generic message to prevent email enumeration. + No email is sent when the account does not exist or is already confirmed. + If the account exists and is unconfirmed, a new token is generated and + any previous confirmation token is invalidated. + operationId: authResendConfirmation security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: userId - in: path - required: true - schema: - type: string - format: uuid + - AnonKey: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - email + properties: + email: + type: string + format: email responses: - '204': - description: User deleted - /projects/{id}/auth/users/{userId}/sessions: - get: + '200': + description: Generic success message + content: + application/json: + schema: + type: object + properties: + message: + type: string + '429': + description: Rate limit exceeded + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /auth/signup-anonymous: + post: tags: - - Auth Admin - summary: List user sessions + - Authentication + summary: Create anonymous user description: | - List paginated sessions for a specific auth user. - Returns session details including device info, IP address, and activity timestamps. + Create guest user without email/password. - Ordering and pagination match `GET /auth/user/sessions`: the default is - activity order with `page`/`limit` and the legacy `sessions` body, and - `sort=created_at` opts into the standard cursor/offset hybrid with the - shared `data` envelope. Cursor pagination is only available for - `sort=created_at`, because the activity timestamp changes under paging. - The `status=expired` filter is offset-only because sessions can expire - above a cursor anchor during a walk. - operationId: listUserSessions + User metadata (like display_name) can be included and will appear in realtime presence events. + Requires enable_anonymous_signins to be true. + operationId: authSignupAnonymous security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: userId - in: path - required: true - schema: - type: string - format: uuid - - name: page - in: query - description: Page number (1-indexed) - schema: - type: integer - minimum: 1 - default: 1 - - name: limit - in: query - description: Number of sessions per page (max 100) - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: sort - in: query - description: | - Sort key. `last_activity` (default) orders by most recent activity and - supports offset pagination only. `created_at` orders by session start - and supports both offset and cursor pagination. - schema: - type: string - enum: - - last_activity - - created_at - default: last_activity - - name: status - in: query - description: | - Filter by whether the session can still be refreshed. Omit for every - stored session, including expired ones. `expired` is not supported - with cursor pagination. - schema: - type: string - enum: - - active - - expired - - name: cursor - in: query - description: | - Opaque keyset cursor from a previous response's `next_cursor`. Requires - `sort=created_at`; mutually exclusive with `page` and `ending_before`. - schema: - type: string - - name: ending_before - in: query - description: | - Opaque keyset cursor from a previous response's `prev_cursor`, paging - backward. Requires `sort=created_at`; mutually exclusive with `page` - and `cursor`. - schema: - type: string - - name: offset - in: query - description: | - Bounded number of rows to skip past the cursor anchor (the hybrid - jump, maximum 100000). Ignored unless `cursor` or `ending_before` is - supplied. - schema: - type: integer - minimum: 0 - maximum: 100000 + - AnonKey: [] + requestBody: + description: Optional user metadata + content: + application/json: + schema: + type: object + properties: + user_metadata: + type: object + additionalProperties: true + description: Custom user metadata (e.g., display_name, avatar_url) + example: + display_name: Alice + avatar_url: https://example.com/alice.jpg responses: - '200': - description: Paginated list of user sessions - content: - application/json: - schema: - type: object - properties: - sessions: - type: array - items: - $ref: '#/components/schemas/AuthSession' - total: - type: integer - description: Total number of sessions - page: - type: integer - description: Current page number - limit: - type: integer - description: Number of sessions per page - total_pages: - type: integer - description: Total number of pages - data: - type: array - description: Sessions for this page (cursor pagination only) - items: - $ref: '#/components/schemas/AuthSession' - has_more: - type: boolean - description: Whether a further page exists (cursor pagination only) - next_cursor: - type: string - description: Opaque cursor for the next page (cursor pagination only) - prev_cursor: - type: string - description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. - '400': - description: Invalid or conflicting pagination parameters + '201': + description: Anonymous user created content: application/json: schema: - $ref: '#/components/schemas/Error' - '404': - description: User not found - delete: - tags: - - Auth Admin - summary: Delete all user sessions - description: | - Revokes all sessions for a user, forcing them to re-authenticate on all devices. - Use this to log out a user from everywhere. - operationId: deleteAllUserSessions - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: userId - in: path - required: true - schema: - type: string - format: uuid - responses: - '204': - description: All sessions deleted - '404': - description: User not found - /projects/{id}/auth/users/{userId}/sessions/{sessionId}: - delete: - tags: - - Auth Admin - summary: Delete specific session - description: | - Revokes a specific session for a user. - Use this to log out a user from a single device. - operationId: deleteUserSession - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: userId - in: path - required: true - schema: - type: string - format: uuid - - name: sessionId - in: path - required: true - schema: - type: string - format: uuid - responses: - '204': - description: Session deleted - '404': - description: Session or user not found - /projects/{id}/auth/users/{userId}/ban: + $ref: '#/components/schemas/AuthTokenResponse' + '403': + description: Anonymous signins disabled + /auth/user/convert-anonymous: post: tags: - - Auth Admin - summary: Ban a user + - Authentication + summary: Convert anonymous user to authenticated description: | - Bans a user temporarily or permanently. Banned users cannot sign in - and all their active sessions are immediately revoked. - - - Omit `banned_until` for a permanent ban - - Provide `banned_until` ISO timestamp for a temporary ban - operationId: banAuthUser + Add email and password to anonymous user. + Requires auth user access token. + If require_email_confirmation is enabled for the project, the converted + user remains unconfirmed until /auth/confirm succeeds. When email + sending is enabled, a confirmation email is sent during conversion. + operationId: authConvertAnonymous security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: userId - in: path - required: true - schema: - type: string - format: uuid + - AuthUserAccessToken: [] requestBody: + required: true content: application/json: schema: type: object + required: + - email + - password properties: - banned_until: + email: type: string - format: date-time - description: When the ban expires (omit for permanent ban) - example: '2026-12-31T23:59:59Z' + format: email + password: + type: string + description: | + Password validated after NFC normalization against the + policy returned by GET /auth/password-policy. + user_metadata: + type: object + additionalProperties: true responses: '200': - description: User banned successfully - content: - application/json: - schema: - $ref: '#/components/schemas/BanUserResponse' - '404': - description: User not found + description: User converted successfully content: application/json: schema: - $ref: '#/components/schemas/Error' - /projects/{id}/auth/users/{userId}/unban: - post: - tags: - - Auth Admin - summary: Unban a user - description: | - Removes a ban from a user, restoring their ability to sign in. - The user's status is set back to 'active'. - operationId: unbanAuthUser - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: userId - in: path - required: true - schema: - type: string - format: uuid - responses: - '200': - description: User unbanned successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UnbanUserResponse' - '404': - description: User not found + type: object + properties: + user: + $ref: '#/components/schemas/AuthUser' + '400': + description: Not an anonymous user + '403': + description: | + The chosen email domain is not in `allowed_email_domains` + '409': + description: Email already in use + '503': + description: Compromised-password screening is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/email-templates: - get: - tags: - - Auth Configuration - summary: List email templates - description: Returns all custom email templates for this project. - operationId: listEmailTemplates - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - responses: - '200': - description: Email templates list - content: - application/json: - schema: - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/EmailTemplate' + /auth/user/change-email: post: tags: - - Auth Configuration - summary: Create email template + - Authentication + summary: Request email change description: | - Creates a custom email template for the project. Custom email templates - are a PRO-plan feature: requests from a FREE-plan project owner are - rejected with 403, and FREE projects always send the built-in default - templates regardless of any previously saved custom rows. - Every project is created with one template per type, so customizing one - is usually a PUT; creating a type the project already has returns 409. - Valid template types: welcome, confirmation, password_reset, password_changed - operationId: createEmailTemplate + Request to change user's email address. + Sends confirmation token to new email address. + operationId: authRequestEmailChange security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' + - AuthUserAccessToken: [] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/CreateEmailTemplateRequest' + type: object + required: + - new_email + properties: + new_email: + type: string + format: email responses: - '201': - description: Template created + '200': + description: Confirmation email sent content: application/json: schema: - $ref: '#/components/schemas/EmailTemplate' + type: object + properties: + message: + type: string + new_email: + type: string '400': - description: Invalid template type + description: Invalid email format or same as current email + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: Custom email templates require the PRO plan + description: | + The requested email domain is not in `allowed_email_domains` content: application/json: schema: $ref: '#/components/schemas/Error' '409': - description: The project already has a template of this type + description: Email already in use content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/email-templates/{type}: - get: - tags: - - Auth Configuration - summary: Get email template - operationId: getEmailTemplate - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: type - in: path - required: true - schema: - type: string - enum: - - welcome - - confirmation - - password_reset - - password_changed - responses: - '200': - description: Email template + '429': + description: Rate limit exceeded (10 requests per hour per IP) content: application/json: schema: - $ref: '#/components/schemas/EmailTemplate' - '404': - description: Template not found - put: + $ref: '#/components/schemas/Error' + /auth/user/confirm-email-change: + post: tags: - - Auth Configuration - summary: Update email template - description: | - Updates a custom email template. Custom email templates are a PRO-plan - feature: requests from a FREE-plan project owner are rejected with 403 - (including after a PRO→FREE downgrade), so a FREE project cannot modify - templates and always sends the built-in defaults. - operationId: updateEmailTemplate + - Authentication + summary: Confirm email change + description: Confirm email change with token sent to new address + operationId: authConfirmEmailChange security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: type - in: path - required: true - schema: - type: string - enum: - - welcome - - confirmation - - password_reset - - password_changed + - AuthUserAccessToken: [] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateEmailTemplateRequest' + type: object + required: + - email_change_token + properties: + email_change_token: + type: string responses: '200': - description: Template updated + description: Email changed successfully content: application/json: schema: - $ref: '#/components/schemas/EmailTemplate' - '403': - description: Custom email templates require the PRO plan + type: object + properties: + message: + type: string + user: + $ref: '#/components/schemas/AuthUser' + '400': + description: Invalid or expired token, or no pending email change + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Template not found - delete: - tags: - - Auth Configuration - summary: Delete email template - description: | - Deletes a custom template, reverting to the default. Custom email - templates are a PRO-plan feature: requests from a FREE-plan project owner - are rejected with 403. - operationId: deleteEmailTemplate - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: type - in: path - required: true - schema: - type: string - enum: - - welcome - - confirmation - - password_reset - - password_changed - responses: - '204': - description: Template deleted '403': - description: Custom email templates require the PRO plan + description: | + The pending email domain is no longer in `allowed_email_domains`. + Re-checked here because the allowlist can narrow between the request + and the confirmation. content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Template not found - /email-templates/defaults: - get: + '409': + description: Email is now in use by another user + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /auth/user/cancel-email-change: + delete: tags: - - Auth Configuration - summary: Get default email templates - description: Returns the default email templates used when no custom template is configured. - operationId: getDefaultEmailTemplates + - Authentication + summary: Cancel pending email change + operationId: authCancelEmailChange + security: + - AuthUserAccessToken: [] responses: '200': - description: Default templates + description: Email change cancelled content: application/json: schema: type: object properties: - data: - type: array - items: - $ref: '#/components/schemas/EmailTemplate' - /email-templates/defaults/{type}: - get: - tags: - - Auth Configuration - summary: Get default email template by type - operationId: getDefaultEmailTemplate + message: + type: string + '401': + description: Not authenticated + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /auth/user/sessions: + get: + tags: + - Authentication + summary: Get current user's sessions + description: | + Returns paginated sessions for the currently authenticated user. + Each session includes device info, IP addresses, and activity timestamps. + The current session is marked with `is_current: true`. + + **Ordering and pagination.** Without `sort`, results are ordered by most + recent activity and paged with `page`/`limit`, returning the + `sessions`/`total`/`page`/`limit`/`total_pages` body below. This is the + legacy default and is preserved for existing clients. + + Send `sort=created_at` to opt into the standard list contract: results are + ordered by session start (newest first) and may be paged either with + `page`/`limit` or by cursor with `cursor`/`ending_before` plus a bounded + `offset` past the cursor anchor. Cursor responses use the shared + `data` envelope with `next_cursor`/`prev_cursor`. + + Unlike other list endpoints, sending `limit` without `page` does **not** + select cursor mode here; `sort=created_at` is the only opt-in. Cursor + pagination is rejected with 400 for the activity order, because + `last_activity_at` changes whenever a session refreshes its token: a row + that crosses the cursor anchor between two requests would be skipped and + never shown. The `status=expired` filter is also offset-only because a + session can expire above the cursor anchor during a walk. Sending that + filter in cursor mode, `page` with `cursor` or `ending_before`, or both + cursor directions returns 400. + operationId: authGetMySessions + security: + - AuthUserAccessToken: [] parameters: - - name: type - in: path - required: true + - name: page + in: query + description: Page number (1-indexed) + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of sessions per page (max 100) + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: sort + in: query + description: | + Sort key. `last_activity` (default) orders by most recent activity and + supports offset pagination only. `created_at` orders by session start + and supports both offset and cursor pagination. schema: type: string enum: - - welcome - - confirmation - - password_reset - - password_changed + - last_activity + - created_at + default: last_activity + - name: status + in: query + description: | + Filter by whether the session can still be refreshed. Omit for every + stored session, including expired ones. `expired` is not supported + with cursor pagination. + schema: + type: string + enum: + - active + - expired + - name: cursor + in: query + description: | + Opaque keyset cursor from a previous response's `next_cursor`. Requires + `sort=created_at`; mutually exclusive with `page` and `ending_before`. + schema: + type: string + - name: ending_before + in: query + description: | + Opaque keyset cursor from a previous response's `prev_cursor`, paging + backward. Requires `sort=created_at`; mutually exclusive with `page` + and `cursor`. + schema: + type: string + - name: offset + in: query + description: | + Bounded number of rows to skip past the cursor anchor (the hybrid + jump, maximum 100000). Ignored unless `cursor` or `ending_before` is + supplied. + schema: + type: integer + minimum: 0 + maximum: 100000 responses: '200': - description: Default template + description: Paginated list of user sessions content: application/json: schema: - $ref: '#/components/schemas/EmailTemplate' - '404': - description: Template type not found - /projects/{id}/auth/config: - get: + type: object + properties: + sessions: + type: array + items: + $ref: '#/components/schemas/AuthSession' + total: + type: integer + description: Total number of sessions + page: + type: integer + description: Current page number + limit: + type: integer + description: Number of sessions per page + total_pages: + type: integer + description: Total number of pages + data: + type: array + description: Sessions for this page (cursor pagination only) + items: + $ref: '#/components/schemas/AuthSession' + has_more: + type: boolean + description: Whether a further page exists (cursor pagination only) + next_cursor: + type: string + description: Opaque cursor for the next page (cursor pagination only) + prev_cursor: + type: string + description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. + '400': + description: Invalid or conflicting pagination parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Not authenticated + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: tags: - - Auth Configuration - summary: Get auth configuration - operationId: getAuthConfig + - Authentication + summary: Sign out from all other devices + description: | + Deletes all sessions except the current one. + Use this to log out from all other devices while keeping the current session active. + operationId: authDeleteAllMySessions security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' + - AuthUserAccessToken: [] responses: - '200': - description: Auth configuration + '204': + description: All other sessions deleted + '401': + description: Not authenticated content: application/json: schema: - $ref: '#/components/schemas/AuthConfig' - put: + $ref: '#/components/schemas/Error' + /auth/user/sessions/{sessionId}: + delete: tags: - - Auth Configuration - summary: Update auth configuration + - Authentication + summary: Sign out from specific device description: | - Updates the project's auth configuration. Only the fields present in - the body are changed. - operationId: updateAuthConfig + Deletes a specific session, logging out that device. + You can get session IDs from the list sessions endpoint. + operationId: authDeleteMySession security: - - UserToken: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAuthConfigRequest' + - name: sessionId + in: path + required: true + description: The session ID to delete + schema: + type: string + format: uuid responses: - '200': - description: Configuration updated + '204': + description: Session deleted + '401': + description: Not authenticated content: application/json: schema: - $ref: '#/components/schemas/AuthConfig' - '403': - description: | - The update would turn on, widen, or otherwise edit the email domain - allowlist (`allowed_email_domains`, `allowed_email_domains_mode`) - for a project that is not on the PRO plan. A FREE project keeps - whatever allowlist it already has — parked, enforcing nothing until - it upgrades — and may still remove it, so a downgrade never leaves a - project locked out of its own signups. + $ref: '#/components/schemas/Error' + '404': + description: Session not found content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/auth/config/test-email: - post: + /auth/user: + get: tags: - - Auth Configuration - summary: Send a test email using the project's saved SMTP config - description: | - Sends a diagnostic email to `to_email` using the project's - persisted `auth_config` SMTP credentials. If `html_body` or - `text_body` is supplied, the override path is taken: those - values (plus optional `subject`) are rendered through - html/text templates against the project's `Data` and - used as the body — used by the template editor's "Send Test" - affordance to preview an unsaved template. With both bodies - omitted, a hardcoded diagnostic message is sent and any - `subject` field is ignored. Sending `subject` alone (no - bodies) is rejected with 400 to avoid a silently-dropped - subject or a blank message. Also rejects with 400 if - `email_enabled=false` or `smtp_host` is empty. - operationId: testEmailConfig + - Authentication + summary: Get current user profile + description: Returns authenticated user's profile. Requires access token. + operationId: authGetUser security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TestEmailRequest' + - AuthUserAccessToken: [] responses: '200': - description: Test email sent + description: User profile content: application/json: schema: - $ref: '#/components/schemas/TestEmailResponse' - '400': - description: Invalid request or email delivery not configured + type: object + properties: + user: + $ref: '#/components/schemas/AuthUser' '401': - description: Unauthorized - '403': - description: Forbidden - '404': - description: Project not found - '502': - description: Template render failure or SMTP delivery failed - /projects/{id}/auth/hosted-pages/{pageType}: - get: - tags: - - Auth Configuration - summary: Get hosted auth page - description: | - Returns the saved HTML/CSS for the page type, or `page: null` when the - project has not customized it yet. Always returns `defaults` (the theme - shell to seed an editor with, which is valid input to the update endpoint) - and `runtime` (the script the rendered page runs, plus a preview harness). - operationId: getAuthHostedPage - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: pageType - in: path - required: true - schema: - $ref: '#/components/schemas/HostedAuthPageType' - responses: - '200': - description: Hosted page loaded + description: Not authenticated - access token missing or invalid content: application/json: schema: - $ref: '#/components/schemas/AuthHostedPageResponse' + $ref: '#/components/schemas/Error' put: tags: - - Auth Configuration - summary: Update hosted auth page - description: | - Saves the current HTML/CSS for this page type. - Security validation rejects script tags, javascript: URLs, inline event handlers, iframe/object/embed/meta/link tags in HTML, - and closing style/head tags in CSS. - operationId: updateAuthHostedPage + - Authentication + summary: Update user profile + description: Update password or metadata. Requires access token. + operationId: authUpdateUser security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: pageType - in: path - required: true - schema: - $ref: '#/components/schemas/HostedAuthPageType' + - AuthUserAccessToken: [] requestBody: - required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateAuthHostedPageRequest' + type: object + properties: + password: + type: string + description: | + Password validated after NFC normalization against the + policy returned by GET /auth/password-policy. + user_metadata: + type: object + additionalProperties: true + description: | + Metadata keys to merge into the current user metadata. + Omitted keys remain unchanged; set a key to null to remove it. + Merging is shallow; nested objects replace the stored value for that top-level key. responses: '200': - description: Hosted page updated + description: Profile updated content: application/json: schema: - $ref: '#/components/schemas/AuthHostedPageResponse' + type: object + properties: + user: + $ref: '#/components/schemas/AuthUser' '400': - description: Invalid input or unsafe markup - '401': - description: Unauthorized - '403': - description: Forbidden - '404': - description: Project not found - /projects/{id}/auth/pages/appearance: - get: - tags: - - Auth Configuration - summary: Get managed auth page appearance - operationId: getAuthPageAppearance - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - responses: - '200': - description: Saved appearance and effective plan state + description: Bad request - invalid password or metadata format content: application/json: schema: - $ref: '#/components/schemas/AuthPageAppearanceResponse' + $ref: '#/components/schemas/Error' '401': - description: Unauthorized + description: Not authenticated - access token missing or invalid content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Access denied + '503': + description: Compromised-password screening is temporarily unavailable content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found + /auth/user/identities: + get: + tags: + - Authentication + summary: List the current user's identities + description: | + Returns every real email identity the account owns. An account can own + multiple identities (for example a password identity plus one or more + OAuth identities on different emails). Anonymous accounts have no real + identity and return an empty list. + operationId: authListIdentities + security: + - AuthUserAccessToken: [] + responses: + '200': + description: List of identities content: application/json: schema: - $ref: '#/components/schemas/Error' - '500': - description: Appearance could not be read + $ref: '#/components/schemas/AuthIdentitiesResponse' + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/auth/pages/theme: - put: + /auth/user/identities/{identityId}: + delete: tags: - - Auth Configuration - summary: Save the managed auth page theme - operationId: updateAuthPageTheme + - Authentication + summary: Unlink an identity from the current user + description: | + Removes a non-primary identity and its attached sign-in methods. Refused + when the identity is the account's primary, its only identity, or when + removing it would leave the account with no way to sign in. + operationId: authUnlinkIdentity security: - - UserToken: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAuthPageThemeRequest' + - name: identityId + in: path + required: true + description: The identity ID to unlink + schema: + type: string + format: uuid responses: - '200': - description: Theme saved - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAuthPageThemeRequest' + '204': + description: Identity unlinked '400': - description: Invalid or unreadable theme + description: Identity cannot be unlinked (primary, last, or would remove last sign-in method), or the identity id is malformed content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Unauthorized + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: Plan does not permit customisation + '404': + description: Identity not found content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found + /auth/user/methods: + get: + tags: + - Authentication + summary: List the current user's sign-in methods + description: | + Returns a flat list of every sign-in method the account owns (password, + each OAuth provider, and any active anonymous method), with the primary + method flagged. Password stubs and converted anonymous methods are excluded. + operationId: authListMethods + security: + - AuthUserAccessToken: [] + responses: + '200': + description: List of sign-in methods content: application/json: schema: - $ref: '#/components/schemas/Error' - '500': - description: Theme could not be saved + $ref: '#/components/schemas/AuthMethodsResponse' + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - delete: + /auth/user/methods/{methodId}/promote: + post: tags: - - Auth Configuration - summary: Clear the managed auth page theme - operationId: deleteAuthPageTheme + - Authentication + summary: Set a method as the account's primary + description: | + Promotes the given method to the account's primary sign-in method. The + account's canonical email is re-derived from the promoted method's identity. + Refused for password stubs and converted anonymous methods, and for an + identity whose domain is outside the project's `allowed_email_domains`. + operationId: authPromoteMethod security: - - UserToken: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/ProjectId' + - name: methodId + in: path + required: true + description: The method ID to promote + schema: + type: string + format: uuid responses: - '204': - description: Theme cleared - '401': - description: Unauthorized + '200': + description: The promoted method content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': - description: Plan does not permit customisation + $ref: '#/components/schemas/AuthMethodSummary' + '400': + description: This method cannot be set as primary (password stub, converted anonymous method, or unverified email), or the method id is malformed content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: Project not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Theme could not be cleared - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /projects/{id}/auth/pages/{pageType}/layout: - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: pageType - in: path - required: true - schema: - $ref: '#/components/schemas/HostedAuthPageType' - put: - tags: - - Auth Configuration - summary: Save one managed auth page layout - operationId: updateAuthPageLayout - security: - - UserToken: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAuthPageLayoutRequest' - responses: - '200': - description: Layout saved - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAuthPageLayoutRequest' - '400': - description: Invalid page type or layout - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: Plan does not permit customisation + description: The promoted identity's email domain is not in the project's allowed_email_domains content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Project not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Layout could not be saved + description: Method not found content: application/json: schema: $ref: '#/components/schemas/Error' - delete: + /projects/{id}/auth/insights: + get: tags: - - Auth Configuration - summary: Clear one managed auth page layout - operationId: deleteAuthPageLayout + - Auth Admin + summary: Get auth user insights + description: | + Returns current auth-user totals, rolling 30-day active users, and + zero-filled signup and successful sign-in counts for an inclusive UTC + date range. Weeks start on Monday. Sign-in counts and active-user + activity begin when collection is deployed. Historical signup counts + are backfilled from users present at deployment. Token refreshes affect + active users but not the sign-in series. + operationId: getAuthInsights security: - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: from + in: query + description: Inclusive UTC start date. Defaults to 29 days before `to`. + schema: + type: string + format: date + - name: to + in: query + description: Inclusive UTC end date. Defaults to today. + schema: + type: string + format: date + - name: interval + in: query + description: Chart bucket size. Defaults to `day`. + schema: + $ref: '#/components/schemas/AuthInsightsInterval' responses: - '204': - description: Layout cleared + '200': + description: Auth insights retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/AuthInsightsResponse' + example: + project_id: 4f165080-a931-4e03-b3bd-41c45c3f0058 + observed_at: '2026-07-20T18:00:00Z' + window: + from: '2026-06-21' + to: '2026-07-20' + interval: day + summary: + total_users: 1234 + active_users_30d: 418 + series: + - bucket_start: '2026-07-20' + signups: 12 + signins: 97 + is_partial: true '400': - description: Invalid page type + description: Invalid date range or interval content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Unauthorized + description: Unauthorized - invalid or missing token content: application/json: schema: $ref: '#/components/schemas/Error' '403': - description: Plan does not permit customisation + description: Access denied content: application/json: schema: @@ -9256,405 +9474,407 @@ paths: schema: $ref: '#/components/schemas/Error' '500': - description: Layout could not be cleared + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/auth/pages/{pageType}/preview: + /projects/{id}/auth/users: get: tags: - - Auth Configuration - summary: Render a short-lived managed auth page preview - description: | - Public HTML endpoint for a preview URL returned by the POST operation. - The signed ticket contains the unsaved appearance, expires shortly, and - runs the production page runtime against mocked authentication responses. - operationId: renderAuthPagePreview - security: [] + - Auth Admin + summary: List all auth users (admin) + description: List auth users in project. Requires platform token. + operationId: listAuthUsers + security: + - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: pageType - in: path - required: true - schema: - $ref: '#/components/schemas/HostedAuthPageType' - - name: ticket + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' + - name: status in: query - required: true + required: false + description: Filter by effective status. `banned` returns only currently-banned users; an expired temporary ban lists as `active`. schema: type: string - minLength: 1 - maxLength: 4096 - description: Short-lived signed preview ticket returned by the POST operation. + enum: + - active + - banned responses: '200': - description: Rendered preview document - content: - text/html: - schema: - type: string - '404': - description: Ticket is invalid or expired, its path does not match, or managed authentication is disabled + description: Successful response content: - text/plain: + application/json: schema: - type: string - '500': - description: Preview could not be rendered + $ref: '#/components/schemas/PaginatedAuthUsers' + '400': + description: Invalid status or pagination parameters content: - text/plain: + application/json: schema: - type: string - post: + $ref: '#/components/schemas/Error' + /projects/{id}/auth/users/{userId}: + get: tags: - - Auth Configuration - summary: Preview an unsaved managed auth page appearance - operationId: previewAuthPage + - Auth Admin + summary: Get specific auth user (admin) + operationId: getAuthUser security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: pageType + - name: userId in: path required: true schema: - $ref: '#/components/schemas/HostedAuthPageType' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PreviewAuthPageRequest' + type: string + format: uuid responses: '200': - description: Short-lived URL for the rendered preview document - content: - application/json: - schema: - $ref: '#/components/schemas/PreviewAuthPageResponse' - '400': - description: Invalid page type or draft - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Access denied - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '404': - description: Project not found or managed authentication is disabled - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Preview could not be rendered + description: Auth user details content: application/json: schema: - $ref: '#/components/schemas/Error' - /projects/{id}/auth/hosted/{pageType}: - get: + $ref: '#/components/schemas/AuthUser' + delete: tags: - - Auth Configuration - summary: Render a managed auth page - description: | - Public HTML endpoint for signup, forgot-password, device approval, - verify-email, and reset-password pages. Login uses the path without a - page type. - Requires `Accept: text/html`. - Returns 404 when managed hosted pages are disabled for the project. - security: [] - operationId: renderManagedAuthPage + - Auth Admin + summary: Delete auth user (admin) + description: Soft-deletes user and revokes all sessions + operationId: deleteAuthUser + security: + - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: pageType + - name: userId in: path required: true schema: - $ref: '#/components/schemas/HostedRenderablePageType' + type: string + format: uuid responses: - '200': - description: Hosted auth page HTML - content: - text/html: - schema: - type: string - '400': - description: Invalid project id or unsupported Accept header - '404': - description: Managed pages disabled or page type not found - /projects/{id}/auth/hosted: + '204': + description: User deleted + /projects/{id}/auth/users/{userId}/sessions: get: tags: - - Auth Configuration - summary: Render default managed auth page + - Auth Admin + summary: List user sessions description: | - Public HTML endpoint for the managed login page. - Requires `Accept: text/html`. - security: [] - operationId: renderDefaultManagedAuthPage + List paginated sessions for a specific auth user. + Returns session details including device info, IP address, and activity timestamps. + + Ordering and pagination match `GET /auth/user/sessions`: the default is + activity order with `page`/`limit` and the legacy `sessions` body, and + `sort=created_at` opts into the standard cursor/offset hybrid with the + shared `data` envelope. Cursor pagination is only available for + `sort=created_at`, because the activity timestamp changes under paging. + The `status=expired` filter is offset-only because sessions can expire + above a cursor anchor during a walk. + operationId: listUserSessions + security: + - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: action + - name: userId + in: path + required: true + schema: + type: string + format: uuid + - name: page in: query - required: false + description: Page number (1-indexed) + schema: + type: integer + minimum: 1 + default: 1 + - name: limit + in: query + description: Number of sessions per page (max 100) + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + - name: sort + in: query + description: | + Sort key. `last_activity` (default) orders by most recent activity and + supports offset pagination only. `created_at` orders by session start + and supports both offset and cursor pagination. schema: type: string enum: - - login - - signup - - forgot-password - - device - description: | - Optional deep-link action for the unified hosted page. Ignored when a - custom login page is configured. `action=device` renders the device - authorization approval UI inline (no redirect to any external app); - it signs the user in and calls `POST /auth/device/verify`. - - name: user_code + - last_activity + - created_at + default: last_activity + - name: status in: query - required: false + description: | + Filter by whether the session can still be refreshed. Omit for every + stored session, including expired ones. `expired` is not supported + with cursor pagination. schema: type: string - description: Device user code (from `POST /auth/device/authorize`) used with `action=device`. - - name: anon_key + enum: + - active + - expired + - name: cursor in: query - required: false + description: | + Opaque keyset cursor from a previous response's `next_cursor`. Requires + `sort=created_at`; mutually exclusive with `page` and `ending_before`. schema: type: string - description: Project anon key used by built-in managed auth flows (required for login/signup/device actions). - - name: state + - name: ending_before in: query - required: false + description: | + Opaque keyset cursor from a previous response's `prev_cursor`, paging + backward. Requires `sort=created_at`; mutually exclusive with `page` + and `cursor`. schema: type: string + - name: offset + in: query description: | - Opaque one-time nonce generated by the client SDK before redirecting - here. On successful login/signup it is echoed back in the post-auth - redirect fragment as `state`, so the SDK can bind the returned session - to the flow it initiated (login-CSRF / session-fixation defense). The - SDK rejects a returned session whose `state` does not match. + Bounded number of rows to skip past the cursor anchor (the hybrid + jump, maximum 100000). Ignored unless `cursor` or `ending_before` is + supplied. + schema: + type: integer + minimum: 0 + maximum: 100000 responses: '200': - description: Hosted auth page HTML + description: Paginated list of user sessions content: - text/html: + application/json: schema: - type: string + type: object + properties: + sessions: + type: array + items: + $ref: '#/components/schemas/AuthSession' + total: + type: integer + description: Total number of sessions + page: + type: integer + description: Current page number + limit: + type: integer + description: Number of sessions per page + total_pages: + type: integer + description: Total number of pages + data: + type: array + description: Sessions for this page (cursor pagination only) + items: + $ref: '#/components/schemas/AuthSession' + has_more: + type: boolean + description: Whether a further page exists (cursor pagination only) + next_cursor: + type: string + description: Opaque cursor for the next page (cursor pagination only) + prev_cursor: + type: string + description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. '400': - description: Invalid project id or unsupported Accept header + description: Invalid or conflicting pagination parameters + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Managed pages disabled - /projects/{id}/auth/hosted/login/options: - get: + description: User not found + delete: tags: - - Auth Configuration - summary: Get hosted login runtime options + - Auth Admin + summary: Delete all user sessions description: | - Returns runtime options for the built-in managed login flow. - Requires `anon_key` query parameter. - Rate limited per project and client IP. Excess requests return `429` and `Retry-After`. - security: [] - operationId: getHostedLoginOptions + Revokes all sessions for a user, forcing them to re-authenticate on all devices. + Use this to log out a user from everywhere. + operationId: deleteAllUserSessions + security: + - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: anon_key - in: query + - name: userId + in: path required: true schema: type: string + format: uuid responses: - '200': - description: Hosted login options returned - content: - application/json: - schema: - $ref: '#/components/schemas/HostedLoginOptionsResponse' - '401': - description: Invalid or missing anon key + '204': + description: All sessions deleted '404': - description: Managed pages disabled - '429': - description: Rate limit exceeded - /projects/{id}/auth/hosted/login/check-email: + description: User not found + /projects/{id}/auth/users/{userId}/sessions/{sessionId}: + delete: + tags: + - Auth Admin + summary: Delete specific session + description: | + Revokes a specific session for a user. + Use this to log out a user from a single device. + operationId: deleteUserSession + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: userId + in: path + required: true + schema: + type: string + format: uuid + - name: sessionId + in: path + required: true + schema: + type: string + format: uuid + responses: + '204': + description: Session deleted + '404': + description: Session or user not found + /projects/{id}/auth/users/{userId}/ban: post: tags: - - Auth Configuration - summary: Check whether email exists for hosted login flow + - Auth Admin + summary: Ban a user description: | - Used by the built-in managed login page to branch UI between signin and signup. - Requires anon key in Authorization header. - Rate limited per project and client IP. Excess requests return `429` and `Retry-After`. - security: [] - operationId: hostedLoginCheckEmail + Bans a user temporarily or permanently. Banned users cannot sign in + and all their active sessions are immediately revoked. + + - Omit `banned_until` for a permanent ban + - Provide `banned_until` ISO timestamp for a temporary ban + operationId: banAuthUser + security: + - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: Authorization - in: header + - name: userId + in: path required: true schema: type: string - description: Bearer anon key (`Bearer `) + format: uuid requestBody: - required: true content: application/json: schema: - $ref: '#/components/schemas/HostedLoginEmailCheckRequest' - responses: - '200': - description: Email existence evaluated - content: - application/json: - schema: - $ref: '#/components/schemas/HostedLoginEmailCheckResponse' - '401': - description: Invalid or missing anon key - '429': - description: Rate limit exceeded - /projects/{id}/auth/methods: - get: - tags: - - Auth Configuration - summary: Get all authentication methods - description: | - Returns all configured authentication methods for this project, - including email/password, anonymous, device authorization, and OAuth providers. - operationId: getAuthMethods - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' + type: object + properties: + banned_until: + type: string + format: date-time + description: When the ban expires (omit for permanent ban) + example: '2026-12-31T23:59:59Z' responses: '200': - description: Authentication methods configuration + description: User banned successfully content: application/json: schema: - type: object - properties: - email_password: - type: object - properties: - enabled: - type: boolean - method: - type: string - name: - type: string - anonymous: - type: object - properties: - enabled: - type: boolean - method: - type: string - name: - type: string - oauth_providers: - type: array - items: - type: object - properties: - enabled: - type: boolean - method: - type: string - provider: - type: string - name: - type: string - redirect_url: - type: string - scopes: - type: array - items: - type: string - available_methods: - type: array - items: - type: string - example: - - email_password - - oauth_google - - oauth_github - put: + $ref: '#/components/schemas/BanUserResponse' + '404': + description: User not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/auth/users/{userId}/unban: + post: tags: - - Auth Configuration - summary: Configure authentication methods (unified) + - Auth Admin + summary: Unban a user description: | - Configure all authentication methods in a single request. - At least one method must remain enabled. - operationId: configureAuthMethods + Removes a ban from a user, restoring their ability to sign in. + The user's status is set back to 'active'. + operationId: unbanAuthUser security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - requestBody: - content: - application/json: - schema: - type: object - properties: - enable_email_password: - type: boolean - enable_anonymous: - type: boolean - oauth_providers: - type: array - items: - type: object - properties: - provider: - type: string - enabled: - type: boolean + - name: userId + in: path + required: true + schema: + type: string + format: uuid responses: '200': - description: Methods configured - '400': - description: At least one method must be enabled - /projects/{id}/oauth/configs: + description: User unbanned successfully + content: + application/json: + schema: + $ref: '#/components/schemas/UnbanUserResponse' + '404': + description: User not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/email-templates: get: tags: - - OAuth Configuration - summary: List OAuth configurations - description: List all OAuth provider configurations for this project - operationId: listOAuthConfigs + - Auth Configuration + summary: List email templates + description: Returns all custom email templates for this project. + operationId: listEmailTemplates security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' responses: '200': - description: List of OAuth configurations + description: Email templates list content: application/json: schema: type: object properties: - configs: + data: type: array items: - $ref: '#/components/schemas/OAuthConfig' + $ref: '#/components/schemas/EmailTemplate' post: tags: - - OAuth Configuration - summary: Create OAuth configuration - description: Configure OAuth provider (Google, GitHub, Microsoft, Apple, Device) - operationId: createOAuthConfig + - Auth Configuration + summary: Create email template + description: | + Creates a custom email template for the project. Custom email templates + are a PRO-plan feature: requests from a FREE-plan project owner are + rejected with 403, and FREE projects always send the built-in default + templates regardless of any previously saved custom rows. + Every project is created with one template per type, so customizing one + is usually a PUT; creating a type the project already has returns 409. + Valid template types: welcome, confirmation, password_reset, password_changed + operationId: createEmailTemplate security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' requestBody: @@ -9662,2799 +9882,4993 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateOAuthConfigRequest' + $ref: '#/components/schemas/CreateEmailTemplateRequest' responses: '201': - description: OAuth config created + description: Template created content: application/json: schema: - $ref: '#/components/schemas/OAuthConfig' + $ref: '#/components/schemas/EmailTemplate' + '400': + description: Invalid template type + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Custom email templates require the PRO plan + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '409': - description: Provider already configured - /projects/{id}/oauth/configs/{provider}: + description: The project already has a template of this type + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/email-templates/{type}: get: tags: - - OAuth Configuration - summary: Get OAuth configuration - operationId: getOAuthConfig + - Auth Configuration + summary: Get email template + operationId: getEmailTemplate security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: provider + - name: type in: path required: true schema: type: string enum: - - google - - github - - microsoft - - apple - - device - - name: client_id - in: query - required: false - schema: - type: string - description: Required when `provider=device` to select a specific device client. + - welcome + - confirmation + - password_reset + - password_changed responses: '200': - description: OAuth configuration + description: Email template content: application/json: schema: - $ref: '#/components/schemas/OAuthConfig' + $ref: '#/components/schemas/EmailTemplate' + '404': + description: Template not found put: tags: - - OAuth Configuration - summary: Update OAuth configuration - operationId: updateOAuthConfig + - Auth Configuration + summary: Update email template + description: | + Updates a custom email template. Custom email templates are a PRO-plan + feature: requests from a FREE-plan project owner are rejected with 403 + (including after a PRO→FREE downgrade), so a FREE project cannot modify + templates and always sends the built-in defaults. + operationId: updateEmailTemplate security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: provider + - name: type in: path required: true schema: type: string enum: - - google - - github - - microsoft - - apple - - device - - name: client_id - in: query - required: false - schema: - type: string - description: Required when `provider=device` to select a specific device client. + - welcome + - confirmation + - password_reset + - password_changed requestBody: + required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateOAuthConfigRequest' + $ref: '#/components/schemas/UpdateEmailTemplateRequest' responses: '200': - description: OAuth config updated + description: Template updated content: application/json: schema: - $ref: '#/components/schemas/OAuthConfig' - delete: + $ref: '#/components/schemas/EmailTemplate' + '403': + description: Custom email templates require the PRO plan + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Template not found + delete: tags: - - OAuth Configuration - summary: Delete OAuth configuration - operationId: deleteOAuthConfig + - Auth Configuration + summary: Delete email template + description: | + Deletes a custom template, reverting to the default. Custom email + templates are a PRO-plan feature: requests from a FREE-plan project owner + are rejected with 403. + operationId: deleteEmailTemplate security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: provider + - name: type in: path required: true schema: type: string enum: - - google - - github - - microsoft - - apple - - device - - name: client_id - in: query - required: false - schema: - type: string - description: Required when `provider=device` to select a specific device client. + - welcome + - confirmation + - password_reset + - password_changed responses: '204': - description: OAuth config deleted - /projects/{id}/oauth/providers: + description: Template deleted + '403': + description: Custom email templates require the PRO plan + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Template not found + /email-templates/defaults: get: tags: - - OAuth Configuration - summary: List available OAuth providers - description: Get list of supported OAuth providers and their default scopes - operationId: listAvailableOAuthProviders - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' + - Auth Configuration + summary: Get default email templates + description: Returns the default email templates used when no custom template is configured. + operationId: getDefaultEmailTemplates responses: '200': - description: Available providers + description: Default templates content: application/json: schema: type: object properties: - providers: + data: type: array items: - type: object - properties: - id: - type: string - name: - type: string - default_scopes: - type: array - items: - type: string - /auth/oauth/{provider}/authorize: + $ref: '#/components/schemas/EmailTemplate' + /email-templates/defaults/{type}: get: tags: - - OAuth Authentication - summary: Start OAuth authorization - description: | - Redirects user to OAuth provider for authorization. - Handles CSRF protection with state parameter. - Project is identified via the anon_key query parameter. - operationId: authOAuthAuthorize + - Auth Configuration + summary: Get default email template by type + operationId: getDefaultEmailTemplate parameters: - - name: provider + - name: type in: path required: true schema: type: string enum: - - google - - github - - microsoft - - apple - - name: anon_key - in: query - required: true - schema: - type: string - description: Project anon key (required - identifies the project) - - name: redirect_url - in: query - schema: - type: string - description: | - URL to redirect to after the OAuth flow (optional). Must exactly - match an entry in the project's `allowed_redirect_urls`, including - its query string, or be the project's own managed hosted-auth page - URL. - - name: client_state - in: query - schema: - type: string - maxLength: 255 - description: | - Optional application nonce. It is stored with the server-generated - provider state and echoed to redirect_url as `state`. - - name: response_mode - in: query - schema: - type: string - enum: - - code - description: | - Set to `code` to receive a short-lived authorization code at - redirect_url, then use POST /auth/oauth/exchange to obtain the - session. `redirect_url` is required in this mode. When omitted, the - established session-fragment response is retained for compatibility - with existing clients. + - welcome + - confirmation + - password_reset + - password_changed responses: - '307': - description: Redirect to OAuth provider - '400': - description: | - OAuth provider is disabled for this project, or `redirect_url` is - not registered in `allowed_redirect_urls` + '200': + description: Default template + content: + application/json: + schema: + $ref: '#/components/schemas/EmailTemplate' '404': - description: OAuth provider not configured - /auth/oauth/{provider}/callback: + description: Template type not found + /projects/{id}/auth/config: get: tags: - - OAuth Authentication - summary: OAuth callback handler - description: | - Handles OAuth provider callback with authorization code. - Exchanges code for tokens and creates/signs in user. - operationId: authOAuthCallback + - Auth Configuration + summary: Get auth configuration + operationId: getAuthConfig + security: + - UserToken: [] + - ProjectAccessToken: [] parameters: - - name: provider - in: path - required: true - schema: - type: string - enum: - - google - - github - - microsoft - - apple - - name: code - in: query - required: true - schema: - type: string - - name: state - in: query - required: true - schema: - type: string - - name: error - in: query - schema: - type: string + - $ref: '#/components/parameters/ProjectId' responses: '200': - description: Existing user signed in (when redirect_url was omitted) - content: - application/json: - schema: - $ref: '#/components/schemas/AuthTokenResponse' - '201': - description: New user created and signed in (when redirect_url was omitted) + description: Auth configuration content: application/json: schema: - $ref: '#/components/schemas/AuthTokenResponse' - '303': - description: | - Redirect to the exact registered redirect_url. Flows that requested - response_mode=code receive a short-lived, single-use `code` and - optional application `state`; compatibility flows receive the - established session fragment. - '400': - description: | - Missing/invalid code or state, the state parameter expired, or the - flow's stored redirect_url is no longer registered in - allowed_redirect_urls (re-checked at callback time) - '403': - description: | - The provider's email domain is not in `allowed_email_domains`. Creating - an account is refused under `signup` and `signup_and_signin`; signing in - an already-linked account is refused under `signup_and_signin`. - '409': - description: Email already exists (requires linking) - /auth/oauth/exchange: - post: + $ref: '#/components/schemas/AuthConfig' + put: tags: - - OAuth Authentication - summary: Exchange OAuth authorization code + - Auth Configuration + summary: Update auth configuration description: | - Atomically consumes a short-lived callback code and returns the user's - session. The request must use the same project anon key and exact - redirect_url that initiated the flow. - operationId: authOAuthExchange + Updates the project's auth configuration. Only the fields present in + the body are changed. + operationId: updateAuthConfig security: - - AnonKey: [] + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' requestBody: - required: true content: application/json: schema: - type: object - required: - - code - - redirect_url - properties: - code: - type: string - redirect_url: - type: string - format: uri + $ref: '#/components/schemas/UpdateAuthConfigRequest' responses: '200': - description: Authorization code consumed and session created + description: Configuration updated content: application/json: schema: - $ref: '#/components/schemas/AuthTokenResponse' - '400': - description: Invalid, expired, consumed, or redirect-mismatched code - '401': - description: Missing or invalid project anon key + $ref: '#/components/schemas/AuthConfig' '403': description: | - Email confirmation is now required, or the account's email domain is not - in `allowed_email_domains` while `allowed_email_domains_mode` is - `signup_and_signin`. Both are re-checked here because the code outlives - the callback that issued it. - '429': - description: Too many exchange attempts from this client - /auth/device/authorize: + The update would turn on, widen, or otherwise edit the email domain + allowlist (`allowed_email_domains`, `allowed_email_domains_mode`) + for a project that is not on the PRO plan. A FREE project keeps + whatever allowlist it already has — parked, enforcing nothing until + it upgrades — and may still remove it, so a downgrade never leaves a + project locked out of its own signups. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/auth/config/test-email: post: tags: - - OAuth Authentication - summary: Start RFC8628 device authorization + - Auth Configuration + summary: Send a test email using the project's saved SMTP config description: | - Starts OAuth 2.0 Device Authorization Grant (RFC 8628). - Returns `device_code` for the CLI and `user_code` for browser verification. - - By default the returned `verification_uri` / `verification_uri_complete` - point at the project's managed device-approval page served by this API - (`/projects/{projectId}/auth/hosted?action=device&user_code=...&anon_key=...`), - which requires managed auth enabled and a default anon key for the - project. - - Projects can override this by setting `device_verification_url` on the - auth config (`PATCH /auth/config`). When set, that URL is returned as-is - with the `user_code` appended (no `action=device` hint and no embedded - anon key — the page brings its own), so a CLI's `login` command surfaces - the project's own RFC 8628 approval page. With a custom URL, device login - does **not** require managed auth to be enabled; the custom page's origin - must be in the project's auth CORS allowlist to call - `POST /auth/device/verify`. Either way the verification page must - authenticate the end user and call `POST /auth/device/verify` with the - `user_code`. See the device-auth guide for both approaches. - operationId: authDeviceAuthorize + Sends a diagnostic email to `to_email` using the project's + persisted `auth_config` SMTP credentials. If `html_body` or + `text_body` is supplied, the override path is taken: those + values (plus optional `subject`) are rendered through + html/text templates against the project's `Data` and + used as the body — used by the template editor's "Send Test" + affordance to preview an unsaved template. With both bodies + omitted, a hardcoded diagnostic message is sent and any + `subject` field is ignored. Sending `subject` alone (no + bodies) is rejected with 400 to avoid a silently-dropped + subject or a blank message. Also rejects with 400 if + `email_enabled=false` or `smtp_host` is empty. + operationId: testEmailConfig + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' requestBody: required: true content: application/json: schema: - type: object - required: - - client_id - properties: - client_id: - type: string - description: Enabled `device` OAuth client ID for the target project + $ref: '#/components/schemas/TestEmailRequest' responses: '200': - description: Device authorization started + description: Test email sent content: application/json: schema: - $ref: '#/components/schemas/DeviceAuthorizationResponse' + $ref: '#/components/schemas/TestEmailResponse' '400': - description: Invalid request or unauthorized client + description: Invalid request or email delivery not configured + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Project not found + '502': + description: Template render failure or SMTP delivery failed + /projects/{id}/auth/hosted-pages/{pageType}: + get: + tags: + - Auth Configuration + summary: Get hosted auth page + description: | + Returns the saved HTML/CSS for the page type, or `page: null` when the + project has not customized it yet. Always returns `defaults` (the theme + shell to seed an editor with, which is valid input to the update endpoint) + and `runtime` (the script the rendered page runs, plus a preview harness). + operationId: getAuthHostedPage + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: pageType + in: path + required: true + schema: + $ref: '#/components/schemas/HostedAuthPageType' + responses: + '200': + description: Hosted page loaded content: application/json: schema: - $ref: '#/components/schemas/OAuthErrorResponse' - /auth/device/token: - post: + $ref: '#/components/schemas/AuthHostedPageResponse' + put: tags: - - OAuth Authentication - summary: Poll device token endpoint + - Auth Configuration + summary: Update hosted auth page description: | - RFC8628 token polling endpoint. - Returns OAuth errors such as `authorization_pending`, `slow_down`, `access_denied`, and `expired_token`. - operationId: authDeviceToken + Saves the current HTML/CSS for this page type. + Security validation rejects script tags, javascript: URLs, inline event handlers, iframe/object/embed/meta/link tags in HTML, + and closing style/head tags in CSS. + operationId: updateAuthHostedPage + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: pageType + in: path + required: true + schema: + $ref: '#/components/schemas/HostedAuthPageType' requestBody: required: true content: application/json: schema: - type: object - required: - - grant_type - - device_code - - client_id - properties: - grant_type: - type: string - enum: - - urn:ietf:params:oauth:grant-type:device_code - device_code: - type: string - client_id: - type: string + $ref: '#/components/schemas/UpdateAuthHostedPageRequest' responses: '200': - description: Device flow completed, auth-user session minted + description: Hosted page updated content: application/json: schema: - $ref: '#/components/schemas/AuthTokenResponse' + $ref: '#/components/schemas/AuthHostedPageResponse' '400': - description: Polling state/error response + description: Invalid input or unsafe markup + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Project not found + /projects/{id}/auth/pages/appearance: + get: + tags: + - Auth Configuration + summary: Get managed auth page appearance + operationId: getAuthPageAppearance + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + responses: + '200': + description: Saved appearance and effective plan state content: application/json: schema: - $ref: '#/components/schemas/OAuthErrorResponse' + $ref: '#/components/schemas/AuthPageAppearanceResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '403': - description: | - `access_denied` - the approving account's email domain is not in - `allowed_email_domains` while `allowed_email_domains_mode` is - `signup_and_signin`. Re-checked here because approval and redemption - are separate requests. + description: Access denied content: application/json: schema: - $ref: '#/components/schemas/OAuthErrorResponse' - /auth/device/verify: - post: + $ref: '#/components/schemas/Error' + '404': + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Appearance could not be read + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/auth/pages/theme: + put: tags: - - OAuth Authentication - summary: Approve or deny a device code - description: | - Browser-side endpoint for authenticated auth-users to approve (`approve`) or deny (`deny`) a `user_code`. - - Called by the verification page after the end user signs in. The grant is - scoped to the project the auth-user token belongs to: approving a - `user_code` issued for a different project returns `403`. This endpoint - does not require managed auth to be enabled, so a custom verification page - (hosted anywhere) can drive approval — it just needs an authenticated - project auth-user access token and, for cross-origin browser calls, the - page origin allowed in the project's auth CORS settings. - operationId: authDeviceVerify + - Auth Configuration + summary: Save the managed auth page theme + operationId: updateAuthPageTheme security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' requestBody: required: true content: application/json: schema: - type: object - required: - - user_code - properties: - user_code: - type: string - action: - type: string - enum: - - approve - - deny - default: approve + $ref: '#/components/schemas/UpdateAuthPageThemeRequest' responses: '200': - description: Verification action accepted + description: Theme saved content: application/json: schema: - type: object - properties: - success: - type: boolean - status: - type: string + $ref: '#/components/schemas/UpdateAuthPageThemeRequest' + '400': + description: Invalid or unreadable theme + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': - description: Not authenticated + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/platform/exchange: - post: - tags: - - OAuth Authentication - summary: Exchange auth-user device session for platform token - description: | - Exchanges a verified auth-user device-flow session into a platform token for CLI usage. - The target platform user is derived from authenticated auth-user mapping; client cannot select another user. - operationId: authPlatformExchange - security: - - AuthUserAccessToken: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - client_id - properties: - client_id: - type: string - responses: - '200': - description: Platform token minted + '403': + description: Plan does not permit customisation content: application/json: schema: - $ref: '#/components/schemas/PlatformExchangeResponse' - '403': - description: | - Exchange not allowed for this session/client/project, or the - account's email domain is not in `allowed_email_domains` while - `allowed_email_domains_mode` is `signup_and_signin`. The domain is - re-checked here because the minted platform token outlives the - session it is exchanged from. + $ref: '#/components/schemas/Error' + '404': + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/oauth/providers: - get: + '500': + description: Theme could not be saved + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: tags: - - OAuth Authentication - summary: List user's linked providers - description: Get list of OAuth providers linked to current user - operationId: authListOAuthProviders + - Auth Configuration + summary: Clear the managed auth page theme + operationId: deleteAuthPageTheme security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' responses: - '200': - description: Linked providers + '204': + description: Theme cleared + '401': + description: Unauthorized content: application/json: schema: - type: object - properties: - providers: - type: array - items: - type: object - properties: - provider: - type: string - linked_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - '401': - description: Not authenticated + $ref: '#/components/schemas/Error' + '403': + description: Plan does not permit customisation content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/oauth/{provider}/link: - post: + '404': + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Theme could not be cleared + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/auth/pages/{pageType}/layout: + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: pageType + in: path + required: true + schema: + $ref: '#/components/schemas/HostedAuthPageType' + put: tags: - - OAuth Authentication - summary: Link OAuth provider to current user - description: | - Generates authorization URL to link OAuth provider to existing account. - User must be authenticated. - operationId: authLinkOAuthProvider + - Auth Configuration + summary: Save one managed auth page layout + operationId: updateAuthPageLayout security: - - AuthUserAccessToken: [] - parameters: - - name: provider - in: path - required: true - schema: - type: string - enum: - - google - - github - - microsoft - - apple - - name: redirect_url - in: query - schema: - type: string - description: | - URL to redirect to after linking completes (optional). Same - allowed_redirect_urls requirement as GET /auth/oauth/{provider}/authorize. - - name: client_state - in: query - schema: - type: string - maxLength: 255 - description: | - Optional application nonce echoed to redirect_url as `state`. - - name: response_mode - in: query - schema: - type: string - enum: - - code - description: | - Set to `code` to receive a short-lived authorization code at - redirect_url. `redirect_url` is required in this mode. When - omitted, the established session-fragment response is retained for - compatibility with existing clients. + - UserToken: [] + - ProjectAccessToken: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateAuthPageLayoutRequest' responses: '200': - description: Authorization URL generated + description: Layout saved content: application/json: schema: - type: object - properties: - authorization_url: - type: string + $ref: '#/components/schemas/UpdateAuthPageLayoutRequest' '400': - description: redirect_url is not registered in allowed_redirect_urls + description: Invalid page type or layout content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Not authenticated + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Plan does not permit customisation content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: OAuth provider not configured + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: Provider already linked + '500': + description: Layout could not be saved content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/oauth/{provider}/unlink: delete: tags: - - OAuth Authentication - summary: Unlink OAuth provider - description: | - Remove OAuth provider from user's account. - Cannot unlink if it's the only authentication method. - operationId: authUnlinkOAuthProvider + - Auth Configuration + summary: Clear one managed auth page layout + operationId: deleteAuthPageLayout security: - - AuthUserAccessToken: [] - parameters: - - name: provider - in: path - required: true - schema: - type: string - enum: - - google - - github - - microsoft - - apple + - UserToken: [] + - ProjectAccessToken: [] responses: '204': - description: Provider unlinked + description: Layout cleared '400': - description: Cannot unlink last authentication method + description: Invalid page type content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Not authenticated + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Plan does not permit customisation content: application/json: schema: $ref: '#/components/schemas/Error' '404': - description: Provider not linked + description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error' - /auth/oauth/{provider}/refresh-token: - post: + '500': + description: Layout could not be cleared + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/auth/pages/{pageType}/preview: + get: tags: - - OAuth Authentication - summary: Refresh OAuth provider token + - Auth Configuration + summary: Render a short-lived managed auth page preview description: | - Refresh the access token for an OAuth provider using its refresh token. - Allows calling provider APIs on user's behalf (e.g., Google Drive, GitHub repos). - operationId: refreshOAuthProviderToken - security: - - AuthUserAccessToken: [] + Public HTML endpoint for a preview URL returned by the POST operation. + The signed ticket contains the unsaved appearance, expires shortly, and + runs the production page runtime against mocked authentication responses. + operationId: renderAuthPagePreview + security: [] parameters: - - name: provider + - $ref: '#/components/parameters/ProjectId' + - name: pageType in: path required: true + schema: + $ref: '#/components/schemas/HostedAuthPageType' + - name: ticket + in: query + required: true schema: type: string - enum: - - google - - github - - microsoft - - apple + minLength: 1 + maxLength: 4096 + description: Short-lived signed preview ticket returned by the POST operation. responses: '200': - description: Token refreshed successfully + description: Rendered preview document content: - application/json: + text/html: schema: - type: object - properties: - message: - type: string - provider: - type: string - expires_in: - type: integer - '400': - description: No refresh token available or refresh failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Not authenticated - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + type: string '404': - description: Provider not linked - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /auth/oauth/{provider}/token: - get: - tags: - - OAuth Authentication - summary: Get current provider access token - description: | - Get valid access token for OAuth provider. - Automatically refreshes if expired. - operationId: getOAuthProviderToken - security: - - AuthUserAccessToken: [] - parameters: - - name: provider - in: path - required: true - schema: - type: string - enum: - - google - - github - - microsoft - - apple - responses: - '200': - description: Current access token - content: - application/json: - schema: - type: object - properties: - message: - type: string - provider: - type: string - expires_in: - type: integer - '401': - description: Not authenticated + description: Ticket is invalid or expired, its path does not match, or managed authentication is disabled content: - application/json: + text/plain: schema: - $ref: '#/components/schemas/Error' - '404': - description: Provider not linked + type: string + '500': + description: Preview could not be rendered content: - application/json: + text/plain: schema: - $ref: '#/components/schemas/Error' - /auth/oauth/{provider}/call-api: + type: string post: tags: - - OAuth Authentication - summary: Call OAuth provider API - description: | - Make an authenticated request to an OAuth provider's API on behalf of the user. - The user's stored access token is automatically used and refreshed if needed. - - The request is always sent to the provider's fixed API base URL joined with - the caller-supplied `endpoint`. `endpoint` must be a relative path beginning - with `/` (optionally with a query string); it cannot change the target host. - Absolute URLs, protocol-relative `//host` values, or userinfo (`@host`) are - rejected with `400` so the request can never be redirected to another host. - - Examples of `endpoint`: - - Google userinfo: `/oauth2/v1/userinfo` - - GitHub repositories: `/user/repos` - - Microsoft Graph profile: `/me` - - The response wraps the provider's raw JSON value with request metadata. - An empty provider body is represented as `data: null`; the envelope - preserves the provider's HTTP status in `status_code`, including errors. - Provider response bodies are limited to 8 MiB after decompression. - Transport failures, invalid JSON (including invalid UTF-8), and oversized - bodies return `502`. Provider redirects to another origin are blocked and - return `400`. - operationId: callOAuthProviderAPI + - Auth Configuration + summary: Preview an unsaved managed auth page appearance + operationId: previewAuthPage security: - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: - - name: provider + - $ref: '#/components/parameters/ProjectId' + - name: pageType in: path required: true schema: - type: string - enum: - - google - - github - - microsoft - - apple + $ref: '#/components/schemas/HostedAuthPageType' requestBody: required: true content: application/json: schema: - type: object - required: - - endpoint - properties: - endpoint: - type: string - description: | - Relative path on the provider's API, beginning with `/`. It is - joined with the provider's fixed base URL; it must not contain a - scheme, host, userinfo, or a leading `//`. - example: /user/repos - method: - type: string - enum: - - GET - - POST - default: GET - description: HTTP method to use - body: - type: object - additionalProperties: true - description: Request body for POST requests + $ref: '#/components/schemas/PreviewAuthPageRequest' responses: '200': - description: Provider API response + description: Short-lived URL for the rendered preview document content: application/json: schema: - type: object - description: OAuth provider API response envelope - required: - - provider - - endpoint - - status_code - - data - properties: - provider: - type: string - enum: - - google - - github - - microsoft - - apple - endpoint: - type: string - status_code: - type: integer - minimum: 100 - maximum: 599 - data: - description: Raw provider JSON value, or null when the provider returns no body - nullable: true + $ref: '#/components/schemas/PreviewAuthPageResponse' '400': - description: | - Invalid request (for example: missing `endpoint`, an `endpoint` that is - not a relative path, or an unsupported HTTP method), or a provider - redirect to another origin. + description: Invalid page type or draft content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Not authenticated + description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error' - '404': - description: OAuth provider configuration not found + '403': + description: Access denied content: application/json: schema: $ref: '#/components/schemas/Error' - '500': - description: Failed to create the provider API request + '404': + description: Project not found or managed authentication is disabled content: application/json: schema: $ref: '#/components/schemas/Error' - '502': - description: Provider transport failure, invalid JSON, or response body larger than 8 MiB + '500': + description: Preview could not be rendered content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/anon-keys: + /projects/{id}/auth/hosted/{pageType}: get: tags: - - Anon Keys - summary: List anon keys + - Auth Configuration + summary: Render a managed auth page description: | - Supports two mutually exclusive pagination modes. Offset mode uses `page` - and `limit` and is the default when neither `cursor` nor `search` is - supplied (first page, default `limit`). Cursor mode uses `cursor` and - `limit`, supports `search` (case-insensitive name match), and returns - `next_cursor`. Sending both `page` and `cursor` (or `page` and `search`) - returns 400. - operationId: listAnonKeys - security: - - UserToken: [] + Public HTML endpoint for signup, forgot-password, device approval, + verify-email, and reset-password pages. Login uses the path without a + page type. + Requires `Accept: text/html`. + Returns 404 when managed hosted pages are disabled for the project. + security: [] + operationId: renderManagedAuthPage parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' + - name: pageType + in: path + required: true + schema: + $ref: '#/components/schemas/HostedRenderablePageType' responses: '200': - description: List of anon keys + description: Hosted auth page HTML content: - application/json: + text/html: schema: - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/AnonKey' - total: - type: integer - description: Total number of items matching the query (so the UI can render numbered pages). - has_more: - type: boolean - description: Whether a next page exists. - next_cursor: - type: string - description: Opaque cursor for the next page (cursor pagination only) - prev_cursor: - type: string - description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. - post: + type: string + '400': + description: Invalid project id or unsupported Accept header + '404': + description: Managed pages disabled or page type not found + /projects/{id}/auth/hosted: + get: tags: - - Anon Keys - summary: Create anon key - operationId: createAnonKey - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - name - properties: - name: - type: string - description: | - Key name for identification. - Can only contain letters, numbers, underscores, and hyphens. - pattern: ^[A-Za-z0-9_-]+$ - minLength: 1 - maxLength: 255 - example: frontend-app - permissions: - type: array - items: - type: string - enum: - - auth.signup - - auth.signin - - auth.refresh - - auth.logout - - auth.password_reset - - auth.confirm_email - - auth.resend_confirmation - - storage.upload - - storage.download - - storage.list - - storage.delete - - realtime.connect - - realtime.subscribe - - realtime.publish - - functions.invoke - description: | - Optional list of permissions for this key. - If not provided, defaults to auth-only permissions: auth.signup, auth.signin, auth.refresh, auth.logout, auth.password_reset, auth.confirm_email, auth.resend_confirmation. - Storage, realtime, and functions permissions must be explicitly added if needed. - example: - - auth.signup - - auth.signin - - auth.refresh - - auth.logout - responses: - '201': - description: Anon key created - content: - application/json: - schema: - $ref: '#/components/schemas/AnonKey' - /projects/{id}/anon-keys/{keyId}: - get: - tags: - - Anon Keys - summary: Get anon key - description: Get details of a specific anon key - operationId: getAnonKey - security: - - UserToken: [] + - Auth Configuration + summary: Render default managed auth page + description: | + Public HTML endpoint for the managed login page. + Requires `Accept: text/html`. + security: [] + operationId: renderDefaultManagedAuthPage parameters: - $ref: '#/components/parameters/ProjectId' - - name: keyId - in: path - required: true + - name: action + in: query + required: false schema: type: string - format: uuid - responses: - '200': - description: Anon key details - content: - application/json: - schema: - $ref: '#/components/schemas/AnonKey' - '404': - description: Key not found - delete: - tags: - - Anon Keys - summary: Revoke anon key - description: Revokes key - it will immediately stop working - operationId: revokeAnonKey - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: keyId - in: path - required: true + enum: + - login + - signup + - forgot-password + - device + description: | + Optional deep-link action for the unified hosted page. Ignored when a + custom login page is configured. `action=device` renders the device + authorization approval UI inline (no redirect to any external app); + it signs the user in and calls `POST /auth/device/verify`. + - name: user_code + in: query + required: false schema: type: string - format: uuid + description: Device user code (from `POST /auth/device/authorize`) used with `action=device`. + - name: anon_key + in: query + required: false + schema: + type: string + description: Project anon key used by built-in managed auth flows (required for login/signup/device actions). + - name: state + in: query + required: false + schema: + type: string + description: | + Opaque one-time nonce generated by the client SDK before redirecting + here. On successful login/signup it is echoed back in the post-auth + redirect fragment as `state`, so the SDK can bind the returned session + to the flow it initiated (login-CSRF / session-fixation defense). The + SDK rejects a returned session whose `state` does not match. responses: - '204': - description: Key revoked - '401': - description: Unauthorized - '403': - description: Forbidden - '404': - description: Key not found - '409': - description: Cannot delete the project's default anon key - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error + '200': + description: Hosted auth page HTML content: - application/json: + text/html: schema: - $ref: '#/components/schemas/Error' - /projects/{id}/anon-keys/{keyId}/regenerate: - post: + type: string + '400': + description: Invalid project id or unsupported Accept header + '404': + description: Managed pages disabled + /projects/{id}/auth/hosted/login/options: + get: tags: - - Anon Keys - summary: Regenerate anon key - description: Generate new JWT value for existing key - operationId: regenerateAnonKey - security: - - UserToken: [] + - Auth Configuration + summary: Get hosted login runtime options + description: | + Returns runtime options for the built-in managed login flow. + Requires `anon_key` query parameter. + Rate limited per project and client IP. Excess requests return `429` and `Retry-After`. + security: [] + operationId: getHostedLoginOptions parameters: - $ref: '#/components/parameters/ProjectId' - - name: keyId - in: path + - name: anon_key + in: query required: true schema: type: string - format: uuid responses: '200': - description: Key regenerated + description: Hosted login options returned content: application/json: schema: - $ref: '#/components/schemas/AnonKey' - /projects/{id}/anon-keys/{keyId}/set-default: + $ref: '#/components/schemas/HostedLoginOptionsResponse' + '401': + description: Invalid or missing anon key + '404': + description: Managed pages disabled + '429': + description: Rate limit exceeded + /projects/{id}/auth/hosted/login/check-email: post: tags: - - Anon Keys - summary: Set default anon key - description: Promotes the given key to the project's configured default. At most one key per project can be default. - operationId: setDefaultAnonKey - security: - - UserToken: [] + - Auth Configuration + summary: Check whether email exists for hosted login flow + description: | + Used by the built-in managed login page to branch UI between signin and signup. + Requires anon key in Authorization header. + Rate limited per project and client IP. Excess requests return `429` and `Retry-After`. + security: [] + operationId: hostedLoginCheckEmail parameters: - $ref: '#/components/parameters/ProjectId' - - name: keyId - in: path + - name: Authorization + in: header required: true schema: type: string - format: uuid + description: Bearer anon key (`Bearer `) + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/HostedLoginEmailCheckRequest' responses: '200': - description: Key set as default + description: Email existence evaluated content: application/json: schema: - $ref: '#/components/schemas/AnonKey' + $ref: '#/components/schemas/HostedLoginEmailCheckResponse' '401': - description: Unauthorized - '403': - description: Forbidden - '404': - description: Anon key not found - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /projects/{id}/service-keys: + description: Invalid or missing anon key + '429': + description: Rate limit exceeded + /projects/{id}/auth/methods: get: tags: - - Service Keys - summary: List service keys (paginated) + - Auth Configuration + summary: Get all authentication methods description: | - List all service role keys for a project with pagination. - - **WARNING:** Service keys bypass RLS - for backend/admin use only! - operationId: listServiceKeys + Returns all configured authentication methods for this project, + including email/password, anonymous, device authorization, and OAuth providers. + operationId: getAuthMethods security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Page' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' responses: '200': - description: Paginated list of service keys + description: Authentication methods configuration content: application/json: schema: - $ref: '#/components/schemas/PaginatedServiceKeys' - post: + type: object + properties: + email_password: + type: object + properties: + enabled: + type: boolean + method: + type: string + name: + type: string + anonymous: + type: object + properties: + enabled: + type: boolean + method: + type: string + name: + type: string + oauth_providers: + type: array + items: + type: object + properties: + enabled: + type: boolean + method: + type: string + provider: + type: string + name: + type: string + redirect_url: + type: string + scopes: + type: array + items: + type: string + available_methods: + type: array + items: + type: string + example: + - email_password + - oauth_google + - oauth_github + put: tags: - - Service Keys - summary: Create service key + - Auth Configuration + summary: Configure authentication methods (unified) description: | - Create a new service role key for admin operations. - - **WARNING:** Service keys bypass all RLS policies! - Store securely and NEVER expose in frontend code. - operationId: createServiceKey + Configure all authentication methods in a single request. + At least one method must remain enabled. + operationId: configureAuthMethods security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' requestBody: - required: true content: application/json: schema: type: object - required: - - name properties: - name: - type: string - description: | - Descriptive name for the key (e.g., "admin-dashboard", "background-jobs"). - Can only contain letters, numbers, underscores, and hyphens. - pattern: ^[A-Za-z0-9_-]+$ - minLength: 1 - maxLength: 255 - example: admin-dashboard - permissions: + enable_email_password: + type: boolean + enable_anonymous: + type: boolean + oauth_providers: type: array items: - type: string - description: | - Optional least-privilege scope for the key. When omitted, empty, or - containing only blank strings, the key is granted full access (["*"]) - for backward compatibility. Provide an explicit list (e.g. - ["functions.invoke", "locks.manage"]) to restrict the key; "*" - grants everything. Scope enforcement applies to function invocation, - storage object operations, and project locks. - example: - - functions.invoke - - locks.manage + type: object + properties: + provider: + type: string + enabled: + type: boolean responses: - '201': - description: Service key created - save the key_value immediately! - content: - application/json: - schema: - $ref: '#/components/schemas/ServiceKey' - '409': - description: Key with this name already exists - /projects/{id}/service-keys/{keyId}: + '200': + description: Methods configured + '400': + description: At least one method must be enabled + /projects/{id}/oauth/configs: get: tags: - - Service Keys - summary: Get service key - description: Get details of a specific service key - operationId: getServiceKey + - OAuth Configuration + summary: List OAuth configurations + description: List all OAuth provider configurations for this project + operationId: listOAuthConfigs security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: keyId - in: path - required: true - schema: - type: string - format: uuid responses: '200': - description: Service key details + description: List of OAuth configurations content: application/json: schema: - $ref: '#/components/schemas/ServiceKey' - '404': - description: Key not found - delete: + type: object + properties: + configs: + type: array + items: + $ref: '#/components/schemas/OAuthConfig' + post: tags: - - Service Keys - summary: Delete service key - description: | - Permanently delete a service key. - Any services using this key will immediately lose access. - operationId: deleteServiceKey + - OAuth Configuration + summary: Create OAuth configuration + description: Configure OAuth provider (Google, GitHub, Microsoft, Apple, Device) + operationId: createOAuthConfig security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: keyId - in: path - required: true - schema: - type: string - format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateOAuthConfigRequest' responses: - '204': - description: Key deleted - /projects/{id}/service-keys/{keyId}/regenerate: - post: + '201': + description: OAuth config created + content: + application/json: + schema: + $ref: '#/components/schemas/OAuthConfig' + '409': + description: Provider already configured + /projects/{id}/oauth/configs/{provider}: + get: tags: - - Service Keys - summary: Regenerate service key - description: | - Generate new JWT value for existing key. - The old key is immediately invalidated. - Update your backend services with the new key before regenerating in production. - operationId: regenerateServiceKey + - OAuth Configuration + summary: Get OAuth configuration + operationId: getOAuthConfig security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - name: keyId + - name: provider in: path required: true schema: type: string - format: uuid + enum: + - google + - github + - microsoft + - apple + - device + - name: client_id + in: query + required: false + schema: + type: string + description: Required when `provider=device` to select a specific device client. responses: '200': - description: Key regenerated - save the new key_value immediately! + description: OAuth configuration content: application/json: schema: - $ref: '#/components/schemas/ServiceKey' - /projects/{id}/storage/buckets: - get: + $ref: '#/components/schemas/OAuthConfig' + put: tags: - - Storage Buckets - summary: List all storage buckets in a project - description: | - With no pagination params, returns the full bucket list as a bare array - (legacy). Supplying `cursor`, `ending_before`, `search`, or `limit` - switches to keyset (cursor) pagination and returns a paginated envelope - with `next_cursor`/`prev_cursor` and a filtered `total`. - operationId: listStorageBuckets + - OAuth Configuration + summary: Update OAuth configuration + operationId: updateOAuthConfig security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/Limit' - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple + - device + - name: client_id + in: query + required: false + schema: + type: string + description: Required when `provider=device` to select a specific device client. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateOAuthConfigRequest' responses: '200': - description: | - Either the full bucket list (bare array, legacy) or a paginated - envelope when cursor pagination is requested. + description: OAuth config updated content: application/json: schema: - oneOf: - - type: array - items: - $ref: '#/components/schemas/StorageBucket' - - $ref: '#/components/schemas/PaginatedStorageBuckets' - post: + $ref: '#/components/schemas/OAuthConfig' + delete: tags: - - Storage Buckets - summary: Create a new storage bucket - operationId: createStorageBucket + - OAuth Configuration + summary: Delete OAuth configuration + operationId: deleteOAuthConfig security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateStorageBucketRequest' + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple + - device + - name: client_id + in: query + required: false + schema: + type: string + description: Required when `provider=device` to select a specific device client. responses: - '201': - description: Bucket created - content: - application/json: - schema: - $ref: '#/components/schemas/StorageBucket' - '409': - description: Bucket already exists - /projects/{id}/storage/buckets/{bucketName}: + '204': + description: OAuth config deleted + /projects/{id}/oauth/providers: get: tags: - - Storage Buckets - summary: Get storage bucket by name - operationId: getStorageBucket - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/BucketName' - responses: - '200': - description: Bucket details - content: - application/json: - schema: - $ref: '#/components/schemas/StorageBucket' - '404': - description: Bucket not found - patch: - tags: - - Storage Buckets - summary: Update storage bucket settings - operationId: updateStorageBucket + - OAuth Configuration + summary: List available OAuth providers + description: Get list of supported OAuth providers and their default scopes + operationId: listAvailableOAuthProviders security: - UserToken: [] + - ProjectAccessToken: [] parameters: - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/BucketName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateStorageBucketRequest' responses: '200': - description: Bucket updated + description: Available providers content: application/json: schema: - $ref: '#/components/schemas/StorageBucket' - delete: + type: object + properties: + providers: + type: array + items: + type: object + properties: + id: + type: string + name: + type: string + default_scopes: + type: array + items: + type: string + /auth/oauth/{provider}/authorize: + get: tags: - - Storage Buckets - summary: Delete storage bucket and all objects - operationId: deleteStorageBucket - security: - - UserToken: [] + - OAuth Authentication + summary: Start OAuth authorization + description: | + Redirects user to OAuth provider for authorization. + Handles CSRF protection with state parameter. + Project is identified via the anon_key query parameter. + operationId: authOAuthAuthorize parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/BucketName' + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple + - name: anon_key + in: query + required: true + schema: + type: string + description: Project anon key (required - identifies the project) + - name: redirect_url + in: query + schema: + type: string + description: | + URL to redirect to after the OAuth flow (optional). Must exactly + match an entry in the project's `allowed_redirect_urls`, including + its query string, or be the project's own managed hosted-auth page + URL. + - name: client_state + in: query + schema: + type: string + maxLength: 255 + description: | + Optional application nonce. It is stored with the server-generated + provider state and echoed to redirect_url as `state`. + - name: response_mode + in: query + schema: + type: string + enum: + - code + description: | + Set to `code` to receive a short-lived authorization code at + redirect_url, then use POST /auth/oauth/exchange to obtain the + session. `redirect_url` is required in this mode. When omitted, the + established session-fragment response is retained for compatibility + with existing clients. responses: - '200': - description: Bucket deleted - /projects/{id}/storage/buckets/{bucketName}/policies: + '307': + description: Redirect to OAuth provider + '400': + description: | + OAuth provider is disabled for this project, or `redirect_url` is + not registered in `allowed_redirect_urls` + '404': + description: OAuth provider not configured + /auth/oauth/{provider}/callback: get: tags: - - Storage Policies - summary: List storage policies for a bucket - operationId: listStoragePolicies - security: - - UserToken: [] + - OAuth Authentication + summary: OAuth callback handler + description: | + Handles OAuth provider callback with authorization code. + Exchanges code for tokens and creates/signs in user. + operationId: authOAuthCallback parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/BucketName' + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple + - name: code + in: query + required: true + schema: + type: string + - name: state + in: query + required: true + schema: + type: string + - name: error + in: query + schema: + type: string responses: '200': - description: List of policies + description: Existing user signed in (when redirect_url was omitted) content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/StoragePolicy' + $ref: '#/components/schemas/AuthTokenResponse' + '201': + description: New user created and signed in (when redirect_url was omitted) + content: + application/json: + schema: + $ref: '#/components/schemas/AuthTokenResponse' + '303': + description: | + Redirect to the exact registered redirect_url. Flows that requested + response_mode=code receive a short-lived, single-use `code` and + optional application `state`; compatibility flows receive the + established session fragment. + '400': + description: | + Missing/invalid code or state, the state parameter expired, or the + flow's stored redirect_url is no longer registered in + allowed_redirect_urls (re-checked at callback time) + '403': + description: | + The provider's email domain is not in `allowed_email_domains`. Creating + an account is refused under `signup` and `signup_and_signin`; signing in + an already-linked account is refused under `signup_and_signin`. + '409': + description: Email already exists (requires linking) + /auth/oauth/exchange: post: tags: - - Storage Policies - summary: Create a storage policy - operationId: createStoragePolicy + - OAuth Authentication + summary: Exchange OAuth authorization code + description: | + Atomically consumes a short-lived callback code and returns the user's + session. The request must use the same project anon key and exact + redirect_url that initiated the flow. + operationId: authOAuthExchange security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/BucketName' + - AnonKey: [] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/CreateStoragePolicyRequest' - responses: - '201': - description: Policy created - content: - application/json: - schema: - $ref: '#/components/schemas/StoragePolicy' - /projects/{id}/storage/buckets/{bucketName}/policies/{policyId}: - delete: - tags: - - Storage Policies - summary: Delete a storage policy - operationId: deleteStoragePolicy - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - $ref: '#/components/parameters/BucketName' - - name: policyId - in: path - required: true - schema: - type: string - format: uuid + type: object + required: + - code + - redirect_url + properties: + code: + type: string + redirect_url: + type: string + format: uri responses: '200': - description: Policy deleted - /projects/{id}/storage/objects: - get: + description: Authorization code consumed and session created + content: + application/json: + schema: + $ref: '#/components/schemas/AuthTokenResponse' + '400': + description: Invalid, expired, consumed, or redirect-mismatched code + '401': + description: Missing or invalid project anon key + '403': + description: | + Email confirmation is now required, or the account's email domain is not + in `allowed_email_domains` while `allowed_email_domains_mode` is + `signup_and_signin`. Both are re-checked here because the code outlives + the callback that issued it. + '429': + description: Too many exchange attempts from this client + /auth/device/authorize: + post: tags: - - Storage Admin - summary: List all storage objects in a project + - OAuth Authentication + summary: Start RFC8628 device authorization description: | - Returns a paginated list of all storage objects across all buckets in the project. - Supports filtering by owner and pagination. - operationId: listStorageObjectsAdmin - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - - name: owner_id - in: query - description: Filter by owner user ID - schema: - type: string - format: uuid - - name: page - in: query - description: Page number (1-based) - schema: - type: integer - default: 1 - minimum: 1 - - name: limit - in: query - description: Items per page - schema: - type: integer - default: 50 - minimum: 1 - maximum: 100 - - $ref: '#/components/parameters/Cursor' - - $ref: '#/components/parameters/EndingBefore' - - $ref: '#/components/parameters/Offset' - - $ref: '#/components/parameters/Search' + Starts OAuth 2.0 Device Authorization Grant (RFC 8628). + Returns `device_code` for the CLI and `user_code` for browser verification. + + By default the returned `verification_uri` / `verification_uri_complete` + point at the project's managed device-approval page served by this API + (`/projects/{projectId}/auth/hosted?action=device&user_code=...&anon_key=...`), + which requires managed auth enabled and a default anon key for the + project. + + Projects can override this by setting `device_verification_url` on the + auth config (`PATCH /auth/config`). When set, that URL is returned as-is + with the `user_code` appended (no `action=device` hint and no embedded + anon key — the page brings its own), so a CLI's `login` command surfaces + the project's own RFC 8628 approval page. With a custom URL, device login + does **not** require managed auth to be enabled; the custom page's origin + must be in the project's auth CORS allowlist to call + `POST /auth/device/verify`. Either way the verification page must + authenticate the end user and call `POST /auth/device/verify` with the + `user_code`. See the device-auth guide for both approaches. + operationId: authDeviceAuthorize + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - client_id + properties: + client_id: + type: string + description: Enabled `device` OAuth client ID for the target project responses: '200': - description: Paginated list of storage objects + description: Device authorization started content: application/json: schema: - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/StorageObjectWithBucket' - page: - type: integer - limit: - type: integer - total: - type: integer - has_more: - type: boolean - next_cursor: - type: string - description: Opaque cursor for the next page (cursor pagination only) - prev_cursor: - type: string - description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. - /projects/{id}/storage/stats: - get: - tags: - - Storage Admin - summary: Get storage statistics for a project - description: Returns aggregate storage statistics including bucket count, object count, and total size. - operationId: getStorageStats - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' - responses: - '200': - description: Storage statistics + $ref: '#/components/schemas/DeviceAuthorizationResponse' + '400': + description: Invalid request or unauthorized client content: application/json: schema: - $ref: '#/components/schemas/StorageStats' - /projects/{id}/realtime/config: - get: + $ref: '#/components/schemas/OAuthErrorResponse' + /auth/device/token: + post: tags: - - Realtime - summary: Get realtime configuration for a project - description: Returns the realtime configuration including enabled features and limits. - operationId: getRealtimeConfig - security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' + - OAuth Authentication + summary: Poll device token endpoint + description: | + RFC8628 token polling endpoint. + Returns OAuth errors such as `authorization_pending`, `slow_down`, `access_denied`, and `expired_token`. + operationId: authDeviceToken + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - grant_type + - device_code + - client_id + properties: + grant_type: + type: string + enum: + - urn:ietf:params:oauth:grant-type:device_code + device_code: + type: string + client_id: + type: string responses: '200': - description: Realtime configuration + description: Device flow completed, auth-user session minted content: application/json: schema: - $ref: '#/components/schemas/RealtimeConfig' - '401': - description: Unauthorized + $ref: '#/components/schemas/AuthTokenResponse' + '400': + description: Polling state/error response content: application/json: schema: - $ref: '#/components/schemas/Error' - '404': - description: Project not found + $ref: '#/components/schemas/OAuthErrorResponse' + '403': + description: | + `access_denied` - the approving account's email domain is not in + `allowed_email_domains` while `allowed_email_domains_mode` is + `signup_and_signin`. Re-checked here because approval and redemption + are separate requests. content: application/json: schema: - $ref: '#/components/schemas/Error' - put: + $ref: '#/components/schemas/OAuthErrorResponse' + /auth/device/verify: + post: tags: - - Realtime - summary: Update realtime configuration for a project - description: Updates realtime settings including feature toggles and limits. - operationId: updateRealtimeConfig + - OAuth Authentication + summary: Approve or deny a device code + description: | + Browser-side endpoint for authenticated auth-users to approve (`approve`) or deny (`deny`) a `user_code`. + + Called by the verification page after the end user signs in. The grant is + scoped to the project the auth-user token belongs to: approving a + `user_code` issued for a different project returns `403`. This endpoint + does not require managed auth to be enabled, so a custom verification page + (hosted anywhere) can drive approval — it just needs an authenticated + project auth-user access token and, for cross-origin browser calls, the + page origin allowed in the project's auth CORS settings. + operationId: authDeviceVerify security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' + - AuthUserAccessToken: [] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/UpdateRealtimeConfigRequest' + type: object + required: + - user_code + properties: + user_code: + type: string + action: + type: string + enum: + - approve + - deny + default: approve responses: '200': - description: Updated realtime configuration + description: Verification action accepted content: application/json: schema: - $ref: '#/components/schemas/RealtimeConfig' - '400': - description: Invalid configuration values + type: object + properties: + success: + type: boolean + status: + type: string + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '401': - description: Unauthorized + /auth/platform/exchange: + post: + tags: + - OAuth Authentication + summary: Exchange auth-user device session for platform token + description: | + Exchanges a verified auth-user device-flow session into a platform token for CLI usage. + The target platform user is derived from authenticated auth-user mapping; client cannot select another user. + operationId: authPlatformExchange + security: + - AuthUserAccessToken: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - client_id + properties: + client_id: + type: string + responses: + '200': + description: Platform token minted + content: + application/json: + schema: + $ref: '#/components/schemas/PlatformExchangeResponse' + '403': + description: | + Exchange not allowed for this session/client/project, or the + account's email domain is not in `allowed_email_domains` while + `allowed_email_domains_mode` is `signup_and_signin`. The domain is + re-checked here because the minted platform token outlives the + session it is exchanged from. content: application/json: schema: $ref: '#/components/schemas/Error' - /projects/{id}/realtime/stats: + /auth/oauth/providers: get: tags: - - Realtime - summary: Get realtime statistics for a project - description: Returns realtime usage statistics including connection counts and subscribed tables. - operationId: getRealtimeStats + - OAuth Authentication + summary: List user's linked providers + description: Get list of OAuth providers linked to current user + operationId: authListOAuthProviders security: - - UserToken: [] - parameters: - - $ref: '#/components/parameters/ProjectId' + - AuthUserAccessToken: [] responses: '200': - description: Realtime statistics + description: Linked providers content: application/json: schema: - $ref: '#/components/schemas/RealtimeStats' + type: object + properties: + providers: + type: array + items: + type: object + properties: + provider: + type: string + linked_at: + type: string + format: date-time + updated_at: + type: string + format: date-time '401': - description: Unauthorized + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - /storage/{bucketName}: - get: + /auth/oauth/{provider}/link: + post: tags: - - Storage Objects - summary: List objects in a bucket - operationId: listStorageObjects + - OAuth Authentication + summary: Link OAuth provider to current user + description: | + Generates authorization URL to link OAuth provider to existing account. + User must be authenticated. + operationId: authLinkOAuthProvider security: - - AnonKey: [] - - ServiceRoleKey: [] - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/BucketName' - - name: prefix + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple + - name: redirect_url in: query - description: Filter objects by path prefix schema: type: string - - name: limit + description: | + URL to redirect to after linking completes (optional). Same + allowed_redirect_urls requirement as GET /auth/oauth/{provider}/authorize. + - name: client_state in: query - description: Maximum objects to return schema: - type: integer - default: 50 - maximum: 1000 - - name: cursor + type: string + maxLength: 255 + description: | + Optional application nonce echoed to redirect_url as `state`. + - name: response_mode in: query - description: Pagination cursor schema: type: string + enum: + - code + description: | + Set to `code` to receive a short-lived authorization code at + redirect_url. `redirect_url` is required in this mode. When + omitted, the established session-fragment response is retained for + compatibility with existing clients. responses: '200': - description: List of objects - content: - application/json: - schema: - $ref: '#/components/schemas/StorageListResponse' - '403': - description: Access denied by storage policy - '429': - $ref: '#/components/responses/BandwidthCapExceeded' - /locks/{key}/lease: - post: - tags: - - Locks - summary: Acquire a project lock - description: | - Acquires a project-scoped lease using the project embedded in the service-role key. - The caller must hold the `locks.manage` permission. Repeating the request with the - same lock token is idempotent and resets that lease to the requested TTL. A different - live owner receives `409 lock_held`; a caller whose own lease already lapsed receives - `409 lock_ownership_lost`. - operationId: acquireProjectLock - security: - - ServiceRoleKey: [] - parameters: - - $ref: '#/components/parameters/LockKey' - - $ref: '#/components/parameters/LockToken' - - $ref: '#/components/parameters/LockRequestId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectLockLeaseRequest' - responses: - '201': - description: Lease acquired + description: Authorization URL generated content: application/json: schema: - $ref: '#/components/schemas/ProjectLockLease' + type: object + properties: + authorization_url: + type: string '400': - description: Invalid lock key, token, or TTL + description: redirect_url is not registered in allowed_redirect_urls content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Missing or invalid credentials + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + '404': + description: OAuth provider not configured content: application/json: schema: $ref: '#/components/schemas/Error' '409': - description: | - The lock is held by another live lease (`lock_held`), or the caller's own lease - lapsed and is not yet reclaimable (`lock_ownership_lost`). - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '429': - description: Project lock request limit exceeded - headers: - Retry-After: - description: Seconds until the current fixed-minute window ends. - schema: - type: integer - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '503': - description: Lock service unavailable + description: Provider already linked content: application/json: schema: $ref: '#/components/schemas/Error' - patch: + /auth/oauth/{provider}/unlink: + delete: tags: - - Locks - summary: Renew a project lock + - OAuth Authentication + summary: Unlink OAuth provider description: | - Renews a lease owned by the supplied lock token. The request must arrive - more than one second before `expires_at`; this safety margin prevents - clock skew between regional API instances from resurrecting an expired - lease. - operationId: renewProjectLock + Remove OAuth provider from user's account. + Cannot unlink if it's the only authentication method. + operationId: authUnlinkOAuthProvider security: - - ServiceRoleKey: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/LockKey' - - $ref: '#/components/parameters/LockToken' - - $ref: '#/components/parameters/LockRequestId' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectLockLeaseRequest' + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple responses: - '200': - description: Lease renewed - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectLockLease' + '204': + description: Provider unlinked '400': - description: Invalid lock key, token, or TTL + description: Cannot unlink last authentication method content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Missing or invalid credentials + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + '404': + description: Provider not linked content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: The lease expired or is owned by another token - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '429': - description: Project lock request limit exceeded - headers: - Retry-After: - description: Seconds until the current fixed-minute window ends. - schema: - type: integer - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '503': - description: Lock service unavailable - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - delete: + /auth/oauth/{provider}/refresh-token: + post: tags: - - Locks - summary: Release a project lock - description: Releases a lease only when the supplied lock token still owns it. - operationId: releaseProjectLock + - OAuth Authentication + summary: Refresh OAuth provider token + description: | + Refresh the access token for an OAuth provider using its refresh token. + Allows calling provider APIs on user's behalf (e.g., Google Drive, GitHub repos). + operationId: refreshOAuthProviderToken security: - - ServiceRoleKey: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/LockKey' - - $ref: '#/components/parameters/LockToken' - - $ref: '#/components/parameters/LockRequestId' + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple responses: - '204': - description: Lease released + '200': + description: Token refreshed successfully + content: + application/json: + schema: + type: object + properties: + message: + type: string + provider: + type: string + expires_in: + type: integer '400': - description: Invalid lock key or token + description: No refresh token available or refresh failed content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Missing or invalid credentials + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + '404': + description: Provider not linked content: application/json: schema: $ref: '#/components/schemas/Error' - '409': - description: The lease is owned by another token + /auth/oauth/{provider}/token: + get: + tags: + - OAuth Authentication + summary: Get current provider access token + description: | + Get valid access token for OAuth provider. + Automatically refreshes if expired. + operationId: getOAuthProviderToken + security: + - AuthUserAccessToken: [] + parameters: + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple + responses: + '200': + description: Current access token content: application/json: schema: - $ref: '#/components/schemas/Error' - '429': - description: Project lock request limit exceeded - headers: - Retry-After: - description: Seconds until the current fixed-minute window ends. - schema: - type: integer + type: object + properties: + message: + type: string + provider: + type: string + expires_in: + type: integer + '401': + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Lock service unavailable + '404': + description: Provider not linked content: application/json: schema: $ref: '#/components/schemas/Error' - /locks/{key}: - get: + /auth/oauth/{provider}/call-api: + post: tags: - - Locks - summary: Read a project lock + - OAuth Authentication + summary: Call OAuth provider API description: | - Reports whether the lock is currently held, when its lease expires, and the - holder's fencing token. `held` follows takeover eligibility rather than raw - expiry, so `held: false` means an acquire would succeed now. No lock token is - required, making this usable for monitoring and recovery. - operationId: getProjectLock + Make an authenticated request to an OAuth provider's API on behalf of the user. + The user's stored access token is automatically used and refreshed if needed. + + The request is always sent to the provider's fixed API base URL joined with + the caller-supplied `endpoint`. `endpoint` must be a relative path beginning + with `/` (optionally with a query string); it cannot change the target host. + Absolute URLs, protocol-relative `//host` values, or userinfo (`@host`) are + rejected with `400` so the request can never be redirected to another host. + + Examples of `endpoint`: + - Google userinfo: `/oauth2/v1/userinfo` + - GitHub repositories: `/user/repos` + - Microsoft Graph profile: `/me` + + The response wraps the provider's raw JSON value with request metadata. + An empty provider body is represented as `data: null`; the envelope + preserves the provider's HTTP status in `status_code`, including errors. + Provider response bodies are limited to 8 MiB after decompression. + Transport failures, invalid JSON (including invalid UTF-8), and oversized + bodies return `502`. Provider redirects to another origin are blocked and + return `400`. + operationId: callOAuthProviderAPI security: - - ServiceRoleKey: [] + - AuthUserAccessToken: [] parameters: - - $ref: '#/components/parameters/LockKey' - - $ref: '#/components/parameters/LockRequestId' + - name: provider + in: path + required: true + schema: + type: string + enum: + - google + - github + - microsoft + - apple + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - endpoint + properties: + endpoint: + type: string + description: | + Relative path on the provider's API, beginning with `/`. It is + joined with the provider's fixed base URL; it must not contain a + scheme, host, userinfo, or a leading `//`. + example: /user/repos + method: + type: string + enum: + - GET + - POST + default: GET + description: HTTP method to use + body: + type: object + additionalProperties: true + description: Request body for POST requests responses: '200': - description: Current lock state + description: Provider API response content: application/json: schema: - $ref: '#/components/schemas/ProjectLockState' + type: object + description: OAuth provider API response envelope + required: + - provider + - endpoint + - status_code + - data + properties: + provider: + type: string + enum: + - google + - github + - microsoft + - apple + endpoint: + type: string + status_code: + type: integer + minimum: 100 + maximum: 599 + data: + description: Raw provider JSON value, or null when the provider returns no body + nullable: true '400': - description: Invalid lock key + description: | + Invalid request (for example: missing `endpoint`, an `endpoint` that is + not a relative path, or an unsupported HTTP method), or a provider + redirect to another origin. content: application/json: schema: $ref: '#/components/schemas/Error' '401': - description: Missing or invalid credentials + description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/Error' - '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + '404': + description: OAuth provider configuration not found content: application/json: schema: $ref: '#/components/schemas/Error' - '429': - description: Project lock request limit exceeded - headers: - Retry-After: - description: Seconds until the current fixed-minute window ends. - schema: - type: integer + '500': + description: Failed to create the provider API request content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Lock service unavailable + '502': + description: Provider transport failure, invalid JSON, or response body larger than 8 MiB content: application/json: schema: $ref: '#/components/schemas/Error' - delete: + /projects/{id}/anon-keys: + get: tags: - - Locks - summary: Force release a project lock - description: | - Drops the lease whatever token holds it, for recovering a lock whose holder - died without releasing. Use `DELETE /locks/{key}/lease` for normal release. - - This breaks mutual exclusion by itself: the previous holder keeps working - until its own renewal fails. Guard the protected resource with the lease's - `fencing_token`, which the next acquisition raises, so a write from the - displaced holder can be rejected. Succeeds when the lock is already absent. - operationId: forceReleaseProjectLock + - Anon Keys + summary: List anon keys + description: | + Supports two mutually exclusive pagination modes. Offset mode uses `page` + and `limit` and is the default when neither `cursor` nor `search` is + supplied (first page, default `limit`). Cursor mode uses `cursor` and + `limit`, supports `search` (case-insensitive name match), and returns + `next_cursor`. Sending both `page` and `cursor` (or `page` and `search`) + returns 400. + operationId: listAnonKeys security: - - ServiceRoleKey: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: - - $ref: '#/components/parameters/LockKey' - - $ref: '#/components/parameters/LockRequestId' + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' responses: - '204': - description: Lock released - '400': - description: Invalid lock key + '200': + description: List of anon keys content: application/json: schema: - $ref: '#/components/schemas/Error' - '401': - description: Missing or invalid credentials + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/AnonKey' + total: + type: integer + description: Total number of items matching the query (so the UI can render numbered pages). + has_more: + type: boolean + description: Whether a next page exists. + next_cursor: + type: string + description: Opaque cursor for the next page (cursor pagination only) + prev_cursor: + type: string + description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. + post: + tags: + - Anon Keys + summary: Create anon key + operationId: createAnonKey + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - name + properties: + name: + type: string + description: | + Key name for identification. + Can only contain letters, numbers, underscores, and hyphens. + pattern: ^[A-Za-z0-9_-]+$ + minLength: 1 + maxLength: 255 + example: frontend-app + permissions: + type: array + items: + type: string + enum: + - auth.signup + - auth.signin + - auth.refresh + - auth.logout + - auth.password_reset + - auth.confirm_email + - auth.resend_confirmation + - storage.upload + - storage.download + - storage.list + - storage.delete + - realtime.connect + - realtime.subscribe + - realtime.publish + - functions.invoke + description: | + Optional list of permissions for this key. + If not provided, defaults to auth-only permissions: auth.signup, auth.signin, auth.refresh, auth.logout, auth.password_reset, auth.confirm_email, auth.resend_confirmation. + Storage, realtime, and functions permissions must be explicitly added if needed. + example: + - auth.signup + - auth.signin + - auth.refresh + - auth.logout + responses: + '201': + description: Anon key created content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': - description: A non-service credential was supplied or the service key lacks `locks.manage` + $ref: '#/components/schemas/AnonKey' + /projects/{id}/anon-keys/{keyId}: + get: + tags: + - Anon Keys + summary: Get anon key + description: Get details of a specific anon key + operationId: getAnonKey + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: keyId + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Anon key details content: application/json: schema: - $ref: '#/components/schemas/Error' - '429': - description: Project lock request limit exceeded - headers: - Retry-After: - description: Seconds until the current fixed-minute window ends. - schema: - type: integer + $ref: '#/components/schemas/AnonKey' + '404': + description: Key not found + delete: + tags: + - Anon Keys + summary: Revoke anon key + description: Revokes key - it will immediately stop working + operationId: revokeAnonKey + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: keyId + in: path + required: true + schema: + type: string + format: uuid + responses: + '204': + description: Key revoked + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Key not found + '409': + description: Cannot delete the project's default anon key content: application/json: schema: $ref: '#/components/schemas/Error' - '503': - description: Lock service unavailable + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' - /storage/{bucketName}/move: + /projects/{id}/anon-keys/{keyId}/regenerate: post: tags: - - Storage Objects - summary: Move/rename an object - operationId: moveStorageObject + - Anon Keys + summary: Regenerate anon key + description: Generate new JWT value for existing key + operationId: regenerateAnonKey security: - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: - - $ref: '#/components/parameters/BucketName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/StorageMoveRequest' + - $ref: '#/components/parameters/ProjectId' + - name: keyId + in: path + required: true + schema: + type: string + format: uuid responses: '200': - description: Object moved + description: Key regenerated content: application/json: schema: - $ref: '#/components/schemas/StorageObject' - '403': - description: Access denied by storage policy - '429': - $ref: '#/components/responses/BandwidthCapExceeded' - /storage/{bucketName}/copy: + $ref: '#/components/schemas/AnonKey' + /projects/{id}/anon-keys/{keyId}/set-default: post: tags: - - Storage Objects - summary: Copy an object - operationId: copyStorageObject + - Anon Keys + summary: Set default anon key + description: Promotes the given key to the project's configured default. At most one key per project can be default. + operationId: setDefaultAnonKey security: - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: - - $ref: '#/components/parameters/BucketName' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/StorageCopyRequest' + - $ref: '#/components/parameters/ProjectId' + - name: keyId + in: path + required: true + schema: + type: string + format: uuid responses: - '201': - description: Object copied + '200': + description: Key set as default content: application/json: schema: - $ref: '#/components/schemas/StorageObject' + $ref: '#/components/schemas/AnonKey' + '401': + description: Unauthorized '403': - description: Access denied by storage policy - '429': - $ref: '#/components/responses/BandwidthCapExceeded' - /storage/{bucketName}/{path}: - post: - tags: - - Storage Objects - summary: Upload a file or create resumable session + description: Forbidden + '404': + description: Anon key not found + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/access-tokens: + get: + tags: + - Project Access Tokens + summary: List a project's access tokens description: | - Unified endpoint for file uploads. Behavior depends on Content-Type and headers: - - **Simple Upload (multipart/form-data):** - Upload a complete file in a single request. Best for files under 100MB. + Lists the project's access tokens, newest first. Secrets are never + returned: only a hash is stored, so a token's value exists solely in the + response to the create call. - **Create Resumable Session (application/json):** - Create a session for chunked uploads. Best for large files or unreliable networks. - Requires: `Content-Type: application/json` with body `{"filename": "...", "content_type": "...", "total_size": ...}` - - **Complete Resumable Session:** - Complete a session after all parts are uploaded. - Requires: `X-Upload-Session` header with session ID and `X-Upload-Complete: true` header. + Only tokens that can still authenticate are returned by default, so + revoked and expired ones are hidden. Pass `include_revoked=true` to see + them, which is how you find out what a key did before it stopped working. - **Resumable Session Ownership:** - A session created with a user access token remains bound to that user. A session - created with an anon key remains bound to that exact anon key. Reuse the same - identity or anon key for part uploads, status, completion, and abort requests; - an ownership mismatch returns `404`. - operationId: uploadStorageObject + Requires a platform token. A project access token cannot manage project + access tokens, so a leaked credential cannot enumerate or replace itself. + operationId: listProjectAccessTokens security: - - AnonKey: [] - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] parameters: - - $ref: '#/components/parameters/BucketName' - - name: path - in: path - required: true - description: Object path within bucket - schema: - type: string - - name: X-Upload-Session - in: header - required: false - description: Upload session ID (for completing resumable uploads) - schema: - type: string - - name: X-Upload-Complete - in: header + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Search' + - name: include_revoked + in: query required: false - description: Set to "true" to complete a resumable upload session + description: | + Include tokens that can no longer authenticate — both revoked and + expired ones. schema: - type: string - enum: - - 'true' - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - file - properties: - file: - type: string - format: binary - description: File to upload (simple upload) - application/json: - schema: - $ref: '#/components/schemas/CreateUploadSessionRequest' + type: boolean + default: false responses: '200': - description: Resumable upload completed (when X-Upload-Complete=true) + description: Successful response content: application/json: schema: - $ref: '#/components/schemas/CompleteUploadSessionResponse' - '201': - description: File uploaded or session created + $ref: '#/components/schemas/PaginatedProjectAccessTokens' + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: - oneOf: - - $ref: '#/components/schemas/StorageObject' - - $ref: '#/components/schemas/CreateUploadSessionResponse' - '400': - description: | - Bad request. This can occur when: - - MIME type is not in the bucket's allowed_mime_types list - - File exceeds the bucket's configured file_size_limit - - File exceeds the global maximum upload size (5GB) - - Invalid request body or missing required fields + $ref: '#/components/schemas/Error' '403': - description: Access denied by storage policy + description: Forbidden - not the project owner, or a project access token was used + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Resumable upload session not found or not owned by this credential - '413': - description: | - File size exceeds plan-based limits. This occurs when: - - File exceeds the plan-based maximum file size (FREE or PRO tier) - - Upload would exceed the project's total storage quota - '429': - $ref: '#/components/responses/BandwidthCapExceeded' - put: + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: tags: - - Storage Objects - summary: Upload a part of a resumable upload + - Project Access Tokens + summary: Create a project access token description: | - Upload a single part of a resumable upload session. + Creates a project access token and returns its secret. - **Requirements:** - - Part numbers start at 1 - - All parts except the last must be at least 5MB - - Maximum part size is 25MB - - Parts can be uploaded in any order - - Re-uploading a part overwrites the previous upload - - Anonymous sessions must reuse the exact anon key that created the session - operationId: uploadPart + The secret is in this response and nowhere else. Only its hash is + stored, so it cannot be retrieved, displayed, or recovered later — save + it when you create it. + + The name must be unique within the project, so a retry cannot mint a + second credential. It cannot recover the first one either. A retry that + returns `409` with code `access_token_name_exists` means the original + create committed and its secret is unrecoverable: list the project's + tokens, revoke the one holding that name, and create it again. + + Requires a platform token. + operationId: createProjectAccessToken security: - - AnonKey: [] - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] parameters: - - $ref: '#/components/parameters/BucketName' - - name: path - in: path - required: true - description: Object path within bucket - schema: - type: string - - name: X-Upload-Session - in: header - required: true - description: Upload session ID - schema: - type: string - - name: X-Part-Number - in: header - required: true - description: Part number (1 to 10000) - schema: - type: integer - minimum: 1 - maximum: 10000 + - $ref: '#/components/parameters/ProjectId' requestBody: required: true content: - application/octet-stream: + application/json: schema: - type: string - format: binary + $ref: '#/components/schemas/CreateProjectAccessTokenRequest' responses: - '200': - description: Part uploaded + '201': + description: Token created - save the secret now, it cannot be retrieved again content: application/json: schema: - $ref: '#/components/schemas/UploadSessionPart' + $ref: '#/components/schemas/CreatedProjectAccessToken' '400': - description: Invalid part number or part data + description: Bad request - invalid name, scope, or expiry + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized - invalid or missing token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '403': - description: Access denied + description: Forbidden - not the project owner, or a project access token was used + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Session not found or not owned by this credential + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: | + Duplicate name, token limit reached, or the project is being + deleted. Tell them apart with `code`, which is one of + `access_token_name_exists`, `access_token_limit_reached`, or + `project_deleting` — the message text is not a contract. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/access-tokens/usage: get: tags: - - Storage Objects - summary: Download a file or get upload session status + - Project Access Tokens + summary: Per-day request counts for every access token in a project description: | - Download a file, or get the status of a resumable upload session. + Returns a zero-filled daily series of request counts for each of the + project's access tokens, oldest first. Every day in the window is + present, so a gap reads as zero rather than missing. - **File Download (default):** - Downloads the file at the specified path. + Revoked tokens are included, because the traffic they made before + revocation is usually the reason you are looking. - **Session Status (with X-Upload-Session header):** - Returns the status of a resumable upload session, including which parts have been uploaded. - Anonymous sessions must reuse the exact anon key that created the session. - operationId: downloadStorageObject + `days` defaults to 30 and is capped at 60, which is also how long per-day + counts are retained — a longer window cannot be answered. + + A platform token sees every token in the project. A project access token + sees only its own row, so it can watch its own traffic without being + able to enumerate the project's other credentials by name. + operationId: listProjectAccessTokensUsage security: - - AnonKey: [] - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] + - ProjectAccessToken: [] parameters: - - $ref: '#/components/parameters/BucketName' - - name: path - in: path - required: true - description: Object path within bucket - schema: - type: string - - name: Range - in: header - required: false - description: | - HTTP Range header for partial downloads. - Format: bytes=start-end or bytes=start- - Examples: bytes=0-1023, bytes=1000- - schema: - type: string - pattern: ^bytes=\d+-\d*$ - - name: X-Upload-Session - in: header + - $ref: '#/components/parameters/ProjectId' + - name: days + in: query required: false - description: Upload session ID (to get session status instead of downloading) + description: Number of trailing days to return (1-60, default 30). schema: - type: string + type: integer + minimum: 1 + maximum: 60 + default: 30 responses: '200': - description: File content or session status - headers: - Content-Type: - schema: - type: string - Content-Length: + description: Successful response + content: + application/json: schema: - type: integer - ETag: + type: array + items: + $ref: '#/components/schemas/ProjectAccessTokenUsage' + '400': + description: Bad request - invalid window + content: + application/json: schema: - type: string + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized - invalid or missing token content: - application/octet-stream: + application/json: schema: - type: string - format: binary + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - not the project owner + content: application/json: schema: - $ref: '#/components/schemas/UploadSessionStatusResponse' - '206': - description: Partial content (range request) - '400': - description: Invalid Range header format - '403': - description: Access denied by storage policy + $ref: '#/components/schemas/Error' '404': - description: Object or session not found, or session not owned by this credential - '429': - $ref: '#/components/responses/BandwidthCapExceeded' - delete: + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/access-tokens/{tokenId}: + get: tags: - - Storage Objects - summary: Delete a file or abort upload session + - Project Access Tokens + summary: Get a project access token description: | - Delete a file, or abort a resumable upload session. - - **File Delete (default):** - Deletes the file at the specified path. + Returns one token's metadata. Never its secret, which is not stored in a + recoverable form. - **Abort Session (with X-Upload-Session header):** - Aborts a resumable upload session and cleans up any uploaded parts. - Anonymous sessions must reuse the exact anon key that created the session. - operationId: deleteStorageObject + Requires a platform token. + operationId: getProjectAccessToken security: - - AnonKey: [] - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] parameters: - - $ref: '#/components/parameters/BucketName' - - name: path - in: path - required: true - description: Object path within bucket - schema: - type: string - - name: X-Upload-Session - in: header - required: false - description: Upload session ID (to abort session instead of deleting file) - schema: - type: string + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/TokenId' responses: '200': - description: Object deleted or session aborted + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectAccessToken' + '401': + description: Unauthorized - invalid or missing token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '403': - description: Access denied by storage policy + description: Forbidden - not the project owner, or a project access token was used + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Object or session not found, or session not owned by this credential - '429': - $ref: '#/components/responses/BandwidthCapExceeded' - /storage/{bucketName}/{path}/visibility: - patch: + description: Token not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: tags: - - Storage Objects - summary: Update file visibility (public/private) + - Project Access Tokens + summary: Revoke a project access token description: | - Change whether a file is publicly accessible. Only the file owner or a service key can change visibility. - If the bucket defines UPDATE policies, the owner must also satisfy one of them. + Revokes the token. It stops authenticating immediately in the region + handling this call and within seconds across Volcano's other regions. - - Public files can be downloaded with just an anon key (no user authentication required) - - Private files (default) require authentication and must pass policy checks - - All downloads go through the Volcano API - there is no direct access to the underlying store - operationId: updateStorageObjectVisibility + The record is kept rather than deleted, so the token's name, prefix, last + use, and request history stay available — which is what you need if you + are revoking because a secret leaked. Revoking an already-revoked token + succeeds. + + Revoking does not undo anything the token already did. Treat whatever it + could reach as exposed and rotate accordingly. + + Requires a platform token. + operationId: revokeProjectAccessToken security: - - ServiceRoleKey: [] - - AuthUserAccessToken: [] + - UserToken: [] parameters: - - $ref: '#/components/parameters/BucketName' - - name: path - in: path - required: true - description: Object path within bucket - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/StorageVisibilityRequest' + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/TokenId' responses: - '200': - description: Visibility updated + '204': + description: Token revoked + '401': + description: Unauthorized - invalid or missing token content: application/json: schema: - $ref: '#/components/schemas/StorageObject' + $ref: '#/components/schemas/Error' '403': - description: Not the file owner or denied by the bucket's UPDATE policies + description: Forbidden - not the project owner, or a project access token was used + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Object not found - /public/{projectId}/{bucketName}/{path}: + description: Token not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: Conflict - the project is being deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/access-tokens/{tokenId}/usage: get: tags: - - Storage Objects - summary: Download a public file (no authentication required) + - Project Access Tokens + summary: Per-day request counts for one access token description: | - Download a file that has been marked as public. This endpoint requires NO authentication. - - **Access Requirements:** - - The file must have `is_public: true` set via the visibility endpoint - - Private files will return 403 Forbidden - - **Use Cases:** - - Shareable public URLs for profile pictures, public documents, etc. - - Embedding public files on external websites - - Direct linking without requiring SDK or authentication - - **URL Format:** - ``` - GET /public/{projectId}/{bucketName}/{path} - ``` + Returns a zero-filled daily series of request counts for a single token, + oldest first, so the response always has exactly `days` entries. - **Example:** - ``` - https://api.volcano.dev/public/abc123/avatars/user-photo.jpg - ``` + `days` defaults to 30 and is capped at 60, matching how long per-day + counts are retained. - **CORS:** - This endpoint allows all origins since the file is already public. - operationId: downloadPublicFile + A project access token may read only its own usage; asking for another + token's returns `403`. A platform token may read any token in the + project. + operationId: getProjectAccessTokenUsage + security: + - UserToken: [] + - ProjectAccessToken: [] parameters: - - name: projectId - in: path - required: true - description: Project ID - schema: - type: string - format: uuid - - $ref: '#/components/parameters/BucketName' - - name: path - in: path - required: true - description: Object path within bucket + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/TokenId' + - name: days + in: query + required: false + description: Number of trailing days to return (1-60, default 30). schema: - type: string + type: integer + minimum: 1 + maximum: 60 + default: 30 responses: '200': - description: File content + description: Successful response content: - '*/*': + application/json: schema: - type: string - format: binary - '206': - description: Partial content (range request) + $ref: '#/components/schemas/ProjectAccessTokenUsage' + '400': + description: Bad request - invalid window + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized - invalid or missing token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Forbidden - not the project owner + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': - description: Not found (file doesn't exist or is not public) - '429': - $ref: '#/components/responses/BandwidthCapExceeded' - /health: + description: Token not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/service-keys: get: tags: - - System - summary: Health check endpoint - description: Returns server health status. Used for load balancer and monitoring checks. - operationId: healthCheck + - Service Keys + summary: List service keys (paginated) + description: | + List all service role keys for a project with pagination. + + **WARNING:** Service keys bypass RLS - for backend/admin use only! + operationId: listServiceKeys + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' responses: '200': - description: Server is healthy + description: Paginated list of service keys content: - text/plain: + application/json: schema: - type: string - example: OK -components: - securitySchemes: - AnonKey: - type: http - scheme: bearer - bearerFormat: JWT - description: | - Project-specific public key for frontend authentication. - Required for signup, signin, refresh, and logout endpoints. - Get from Project Settings → Authentication → Anon Keys. - Safe to expose in frontend code (scoped to project, limited permissions). - AuthUserAccessToken: - type: http - scheme: bearer - bearerFormat: JWT + $ref: '#/components/schemas/PaginatedServiceKeys' + post: + tags: + - Service Keys + summary: Create service key description: | - Auth user access token obtained from signup/signin. - Used for authenticated function invocation and user profile access. - Functions invoked with access tokens receive user context in event.__volcano_auth. - Expires after configured lifetime (default: 1 hour). - ServiceRoleKey: - type: http - scheme: bearer - bearerFormat: JWT - description: | - Service role key for admin operations. - **WARNING:** Bypasses Row-Level Security - backend use only! - Create via POST /projects/{id}/service-keys. - Used for function invocation with full database access. - UserToken: - type: http - scheme: bearer - bearerFormat: JWT - description: | - Platform user token from the Management API. - Required for project management operations. - Obtain via POST /tokens in Management API (port 8001). - parameters: - BackupName: - name: backupName - in: path - required: true - schema: - type: string - minLength: 1 - maxLength: 128 - description: | - Backup name, unique within the database, exactly as returned by the list - endpoint. + Create a new service role key for admin operations. - Deliberately looser than the names you can create: a backup made by a - schedule is named for you, so reading or deleting one accepts any name a - backup can have. - BranchName: - name: branchName - in: path - required: true - schema: - type: string - pattern: ^[a-z0-9_]+$ - maxLength: 64 - description: Branch name (unique within the parent database, lowercase letters, numbers, and underscores only) - BucketName: - name: bucketName - in: path - required: true - schema: - type: string - pattern: ^[a-zA-Z0-9_-]+$ - minLength: 1 - maxLength: 64 - description: Storage bucket name - Cursor: - name: cursor - in: query - required: false - schema: - type: string - description: | - Opaque keyset pagination cursor from a previous response's `next_cursor` - — pages forward. Mutually exclusive with `page` and `ending_before`; - combining them returns 400. When supplied, the request's `search` and - `limit` must match the values bound to the cursor or the request returns 400. - EndingBefore: - name: ending_before - in: query - required: false - schema: - type: string + **WARNING:** Service keys bypass all RLS policies! + Store securely and NEVER expose in frontend code. + operationId: createServiceKey + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - name + properties: + name: + type: string + description: | + Descriptive name for the key (e.g., "admin-dashboard", "background-jobs"). + Can only contain letters, numbers, underscores, and hyphens. + pattern: ^[A-Za-z0-9_-]+$ + minLength: 1 + maxLength: 255 + example: admin-dashboard + permissions: + type: array + items: + type: string + description: | + Optional least-privilege scope for the key. When omitted, empty, or + containing only blank strings, the key is granted full access (["*"]) + for backward compatibility. Provide an explicit list (e.g. + ["functions.invoke", "locks.manage"]) to restrict the key; "*" + grants everything. Scope enforcement applies to function invocation, + storage object operations, and project locks. + example: + - functions.invoke + - locks.manage + responses: + '201': + description: Service key created - save the key_value immediately! + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceKey' + '409': + description: Key with this name already exists + /projects/{id}/service-keys/{keyId}: + get: + tags: + - Service Keys + summary: Get service key + description: Get details of a specific service key + operationId: getServiceKey + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: keyId + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Service key details + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceKey' + '404': + description: Key not found + delete: + tags: + - Service Keys + summary: Delete service key description: | - Opaque keyset pagination cursor from a previous response's `prev_cursor` - — pages backward (the page immediately preceding this cursor). Mutually - exclusive with `page` and `cursor`; combining them returns 400. `search` - and `limit` must match the values bound to the cursor or the request - returns 400. - DatabaseName: - name: databaseName - in: path - required: true - schema: - type: string - pattern: ^[a-z0-9_]+$ - maxLength: 64 - description: Database name (unique within project, lowercase letters, numbers, and underscores only) - DeploymentId: - name: deploymentId - in: path - required: true - schema: - type: string - format: uuid - description: Frontend deployment ID - FrontendId: - name: frontendId - in: path - required: true - schema: - type: string - format: uuid - description: Frontend ID - FunctionId: - name: functionId - in: path - required: true - schema: - type: string - format: uuid - description: Function ID - DurableFunctionId: - name: functionId - in: path - required: true - schema: - type: string - description: Durable function ID, or its name within the project - DurableExecutionId: - name: executionId - in: path - required: true - schema: - type: string - format: uuid - description: Durable execution ID - Limit: - name: limit - in: query - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 10 - description: Number of items per page (max 100) - LockKey: - name: key - in: path - required: true - description: Project-local lock name. - schema: - type: string - minLength: 1 - maxLength: 128 - pattern: ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$ - LockToken: - name: X-Volcano-Lock-Token - in: header - required: true - description: Opaque UUID generated once by the caller and retained for the lease lifetime. - schema: - type: string - format: uuid - LockRequestId: - name: X-Volcano-Request-Id - in: header - required: true + Permanently delete a service key. + Any services using this key will immediately lose access. + operationId: deleteServiceKey + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: keyId + in: path + required: true + schema: + type: string + format: uuid + responses: + '204': + description: Key deleted + /projects/{id}/service-keys/{keyId}/regenerate: + post: + tags: + - Service Keys + summary: Regenerate service key description: | - UUID correlating this request across client and server logs. Repeat safety comes from - the lock token, so a retry under a reused request ID still counts against the quota. - schema: - type: string - format: uuid - DeploymentOperation: - name: operation - in: query - required: false - description: Restrict a deployment feed to one kind of operation. - schema: - type: string - enum: - - deploy - - redeploy - - update - - delete - DeploymentOwnerId: - name: owner_id - in: query - required: false - description: | - The user who owns the projects whose deployments to return - (`projects.user_id`). This is ownership, not the actor that started the - deployment — see `initiated_by_user_id` for that. Not a UUID: platform - user ids are opaque strings. - schema: - type: string - maxLength: 255 - DeploymentOrder: - name: order - in: query - required: false - description: | - Sort key and direction. `created_at.desc` (default) is the feed order. - `completed_at.asc` orders finished attempts by completion, oldest first, - and excludes attempts that never completed. - schema: - type: string - enum: - - created_at.desc - - completed_at.asc - default: created_at.desc - DeploymentResourceType: - name: resource_type - in: query - required: false - description: | - Restrict a deployment feed to a single resource type. Omit to return - both Function and Frontend deployments. - schema: - type: string - enum: - - function - - frontend - DeploymentStatus: - name: status - in: query - required: false - description: Restrict a deployment feed to attempts in one status. - schema: - type: string - enum: - - queued - - provisioning - - active - - degraded - - failed - - superseded - - deleting - - deleted - Offset: - name: offset - in: query - required: false - schema: - type: integer - minimum: 0 - default: 0 - description: | - Bounded row offset past the keyset anchor named by `cursor` (forward) or - `ending_before` (backward) — the hybrid jump. Seek to the anchor, then - skip this many rows within. Used for numbered jump-to-page: from the - current page, seek to its next/prev cursor and offset the remaining - pages. Only honored on the cursor pagination path; ignored otherwise. - Page: - name: page - in: query - required: false - schema: - type: integer - minimum: 1 - description: | - Page number (1-indexed) for offset pagination. Declares no schema - default so the request validator does not inject one: handlers that omit - `page` see it unset (nil) and default to 1 in code, while cursor-first - endpoints (e.g. the project deployments feed) can detect its absence to - stay in keyset/search mode. Supplying `page` selects offset pagination. - ProjectId: - name: id - in: path - required: true - schema: - type: string - format: uuid - description: Project ID - Search: - name: search - in: query - required: false - schema: - type: string - maxLength: 256 - description: | - Case-insensitive substring match on the resource `name`. See the - endpoint description for supported pagination modes. - RestoreId: - name: restoreId - in: path - required: true - schema: - type: string - format: uuid - description: Database restore ID - SchedulerId: - name: schedulerId - in: path - required: true - schema: - type: string - format: uuid - description: Function scheduler ID - VariableName: - name: name - in: path - required: true - schema: - type: string - minLength: 1 - maxLength: 256 - pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ - description: Variable name - responses: - BandwidthCapExceeded: - description: | - The platform user exceeded their billing-cycle bandwidth allowance (aggregate - ingress + egress across owned projects). Enforcement is eventual: - requests are rejected until the allowance increases or the next - anniversary cycle begins. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - DatabaseQueryCapExceeded: - description: | - The query was rejected by a billing-cycle allowance: either the owning - platform user's bandwidth allowance (aggregate ingress + egress across - owned projects) or their database-request allowance. Enforcement is - eventual: queries are rejected until the allowance increases or the - next anniversary cycle begins. The error message identifies the resource. - content: - application/json: + Generate new JWT value for existing key. + The old key stops working within a few seconds. + Update your backend services with the new key before regenerating in production. + operationId: regenerateServiceKey + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: keyId + in: path + required: true schema: - $ref: '#/components/schemas/Error' - DatabaseBranchQueryUnavailable: + type: string + format: uuid + responses: + '200': + description: Key regenerated - save the new key_value immediately! + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceKey' + /projects/{id}/storage/buckets: + get: + tags: + - Storage Buckets + summary: List all storage buckets in a project description: | - The branch exists but cannot serve queries: it is still provisioning, - being reset, expired, or its parent is being restored. Distinct from - `404` so a caller waiting on a branch can tell it apart from a typo. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - schemas: + With no pagination params, returns the full bucket list as a bare array + (legacy). Supplying `cursor`, `ending_before`, `search`, or `limit` + switches to keyset (cursor) pagination and returns a paginated envelope + with `next_cursor`/`prev_cursor` and a filtered `total`. + operationId: listStorageBuckets + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: | + Either the full bucket list (bare array, legacy) or a paginated + envelope when cursor pagination is requested. + content: + application/json: + schema: + oneOf: + - type: array + items: + $ref: '#/components/schemas/StorageBucket' + - $ref: '#/components/schemas/PaginatedStorageBuckets' + post: + tags: + - Storage Buckets + summary: Create a new storage bucket + operationId: createStorageBucket + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateStorageBucketRequest' + responses: + '201': + description: Bucket created + content: + application/json: + schema: + $ref: '#/components/schemas/StorageBucket' + '409': + description: Bucket already exists + /projects/{id}/storage/buckets/{bucketName}: + get: + tags: + - Storage Buckets + summary: Get storage bucket by name + operationId: getStorageBucket + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/BucketName' + responses: + '200': + description: Bucket details + content: + application/json: + schema: + $ref: '#/components/schemas/StorageBucket' + '404': + description: Bucket not found + patch: + tags: + - Storage Buckets + summary: Update storage bucket settings + operationId: updateStorageBucket + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/BucketName' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateStorageBucketRequest' + responses: + '200': + description: Bucket updated + content: + application/json: + schema: + $ref: '#/components/schemas/StorageBucket' + delete: + tags: + - Storage Buckets + summary: Delete storage bucket and all objects + operationId: deleteStorageBucket + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/BucketName' + responses: + '200': + description: Bucket deleted + /projects/{id}/storage/buckets/{bucketName}/policies: + get: + tags: + - Storage Policies + summary: List storage policies for a bucket + operationId: listStoragePolicies + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/BucketName' + responses: + '200': + description: List of policies + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/StoragePolicy' + post: + tags: + - Storage Policies + summary: Create a storage policy + operationId: createStoragePolicy + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/BucketName' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateStoragePolicyRequest' + responses: + '201': + description: Policy created + content: + application/json: + schema: + $ref: '#/components/schemas/StoragePolicy' + /projects/{id}/storage/buckets/{bucketName}/policies/{policyId}: + delete: + tags: + - Storage Policies + summary: Delete a storage policy + operationId: deleteStoragePolicy + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - $ref: '#/components/parameters/BucketName' + - name: policyId + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Policy deleted + /projects/{id}/storage/objects: + get: + tags: + - Storage Admin + summary: List all storage objects in a project + description: | + Returns a paginated list of all storage objects across all buckets in the project. + Supports filtering by owner and pagination. + operationId: listStorageObjectsAdmin + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + - name: owner_id + in: query + description: Filter by owner user ID + schema: + type: string + format: uuid + - name: page + in: query + description: Page number (1-based) + schema: + type: integer + default: 1 + minimum: 1 + - name: limit + in: query + description: Items per page + schema: + type: integer + default: 50 + minimum: 1 + maximum: 100 + - $ref: '#/components/parameters/Cursor' + - $ref: '#/components/parameters/EndingBefore' + - $ref: '#/components/parameters/Offset' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: Paginated list of storage objects + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/StorageObjectWithBucket' + page: + type: integer + limit: + type: integer + total: + type: integer + has_more: + type: boolean + next_cursor: + type: string + description: Opaque cursor for the next page (cursor pagination only) + prev_cursor: + type: string + description: Opaque cursor for the previous page (cursor pagination only). Send as `ending_before`. + /projects/{id}/storage/stats: + get: + tags: + - Storage Admin + summary: Get storage statistics for a project + description: Returns aggregate storage statistics including bucket count, object count, and total size. + operationId: getStorageStats + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + responses: + '200': + description: Storage statistics + content: + application/json: + schema: + $ref: '#/components/schemas/StorageStats' + /projects/{id}/realtime/config: + get: + tags: + - Realtime + summary: Get realtime configuration for a project + description: Returns the realtime configuration including enabled features and limits. + operationId: getRealtimeConfig + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + responses: + '200': + description: Realtime configuration + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeConfig' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + tags: + - Realtime + summary: Update realtime configuration for a project + description: Updates realtime settings including feature toggles and limits. + operationId: updateRealtimeConfig + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateRealtimeConfigRequest' + responses: + '200': + description: Updated realtime configuration + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeConfig' + '400': + description: Invalid configuration values + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /projects/{id}/realtime/stats: + get: + tags: + - Realtime + summary: Get realtime statistics for a project + description: Returns realtime usage statistics including connection counts and subscribed tables. + operationId: getRealtimeStats + security: + - UserToken: [] + - ProjectAccessToken: [] + parameters: + - $ref: '#/components/parameters/ProjectId' + responses: + '200': + description: Realtime statistics + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeStats' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /storage/{bucketName}: + get: + tags: + - Storage Objects + summary: List objects in a bucket + operationId: listStorageObjects + security: + - AnonKey: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/BucketName' + - name: prefix + in: query + description: Filter objects by path prefix + schema: + type: string + - name: limit + in: query + description: Maximum objects to return + schema: + type: integer + default: 50 + maximum: 1000 + - name: cursor + in: query + description: Pagination cursor + schema: + type: string + responses: + '200': + description: List of objects + content: + application/json: + schema: + $ref: '#/components/schemas/StorageListResponse' + '403': + description: Access denied by storage policy + '429': + $ref: '#/components/responses/BandwidthCapExceeded' + /locks/{key}/lease: + post: + tags: + - Locks + summary: Acquire a project lock + description: | + Acquires a project-scoped lease using the project embedded in the service-role key. + The caller must hold the `locks.manage` permission. Repeating the request with the + same lock token is idempotent and resets that lease to the requested TTL. A different + live owner receives `409 lock_held`; a caller whose own lease already lapsed receives + `409 lock_ownership_lost`. + operationId: acquireProjectLock + security: + - ServiceRoleKey: [] + parameters: + - $ref: '#/components/parameters/LockKey' + - $ref: '#/components/parameters/LockToken' + - $ref: '#/components/parameters/LockRequestId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectLockLeaseRequest' + responses: + '201': + description: Lease acquired + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectLockLease' + '400': + description: Invalid lock key, token, or TTL + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Missing or invalid credentials + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: A non-service credential was supplied or the service key lacks `locks.manage` + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: | + The lock is held by another live lease (`lock_held`), or the caller's own lease + lapsed and is not yet reclaimable (`lock_ownership_lost`). + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: Project lock request limit exceeded + headers: + Retry-After: + description: Seconds until the current fixed-minute window ends. + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: Lock service unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + tags: + - Locks + summary: Renew a project lock + description: | + Renews a lease owned by the supplied lock token. The request must arrive + more than one second before `expires_at`; this safety margin prevents + clock skew between regional API instances from resurrecting an expired + lease. + operationId: renewProjectLock + security: + - ServiceRoleKey: [] + parameters: + - $ref: '#/components/parameters/LockKey' + - $ref: '#/components/parameters/LockToken' + - $ref: '#/components/parameters/LockRequestId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectLockLeaseRequest' + responses: + '200': + description: Lease renewed + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectLockLease' + '400': + description: Invalid lock key, token, or TTL + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Missing or invalid credentials + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: A non-service credential was supplied or the service key lacks `locks.manage` + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: The lease expired or is owned by another token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: Project lock request limit exceeded + headers: + Retry-After: + description: Seconds until the current fixed-minute window ends. + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: Lock service unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - Locks + summary: Release a project lock + description: Releases a lease only when the supplied lock token still owns it. + operationId: releaseProjectLock + security: + - ServiceRoleKey: [] + parameters: + - $ref: '#/components/parameters/LockKey' + - $ref: '#/components/parameters/LockToken' + - $ref: '#/components/parameters/LockRequestId' + responses: + '204': + description: Lease released + '400': + description: Invalid lock key or token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Missing or invalid credentials + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: A non-service credential was supplied or the service key lacks `locks.manage` + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: The lease is owned by another token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: Project lock request limit exceeded + headers: + Retry-After: + description: Seconds until the current fixed-minute window ends. + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: Lock service unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /locks/{key}: + get: + tags: + - Locks + summary: Read a project lock + description: | + Reports whether the lock is currently held, when its lease expires, and the + holder's fencing token. `held` follows takeover eligibility rather than raw + expiry, so `held: false` means an acquire would succeed now. No lock token is + required, making this usable for monitoring and recovery. + operationId: getProjectLock + security: + - ServiceRoleKey: [] + parameters: + - $ref: '#/components/parameters/LockKey' + - $ref: '#/components/parameters/LockRequestId' + responses: + '200': + description: Current lock state + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectLockState' + '400': + description: Invalid lock key + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Missing or invalid credentials + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: A non-service credential was supplied or the service key lacks `locks.manage` + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: Project lock request limit exceeded + headers: + Retry-After: + description: Seconds until the current fixed-minute window ends. + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: Lock service unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - Locks + summary: Force release a project lock + description: | + Drops the lease whatever token holds it, for recovering a lock whose holder + died without releasing. Use `DELETE /locks/{key}/lease` for normal release. + + This breaks mutual exclusion by itself: the previous holder keeps working + until its own renewal fails. Guard the protected resource with the lease's + `fencing_token`, which the next acquisition raises, so a write from the + displaced holder can be rejected. Succeeds when the lock is already absent. + operationId: forceReleaseProjectLock + security: + - ServiceRoleKey: [] + parameters: + - $ref: '#/components/parameters/LockKey' + - $ref: '#/components/parameters/LockRequestId' + responses: + '204': + description: Lock released + '400': + description: Invalid lock key + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Missing or invalid credentials + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: A non-service credential was supplied or the service key lacks `locks.manage` + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + description: Project lock request limit exceeded + headers: + Retry-After: + description: Seconds until the current fixed-minute window ends. + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '503': + description: Lock service unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /storage/{bucketName}/move: + post: + tags: + - Storage Objects + summary: Move/rename an object + operationId: moveStorageObject + security: + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/BucketName' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/StorageMoveRequest' + responses: + '200': + description: Object moved + content: + application/json: + schema: + $ref: '#/components/schemas/StorageObject' + '403': + description: Access denied by storage policy + '429': + $ref: '#/components/responses/BandwidthCapExceeded' + /storage/{bucketName}/copy: + post: + tags: + - Storage Objects + summary: Copy an object + operationId: copyStorageObject + security: + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/BucketName' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/StorageCopyRequest' + responses: + '201': + description: Object copied + content: + application/json: + schema: + $ref: '#/components/schemas/StorageObject' + '403': + description: Access denied by storage policy + '429': + $ref: '#/components/responses/BandwidthCapExceeded' + /storage/{bucketName}/{path}: + post: + tags: + - Storage Objects + summary: Upload a file or create resumable session + description: | + Unified endpoint for file uploads. Behavior depends on Content-Type and headers: + + **Simple Upload (multipart/form-data):** + Upload a complete file in a single request. Best for files under 100MB. + + **Create Resumable Session (application/json):** + Create a session for chunked uploads. Best for large files or unreliable networks. + Requires: `Content-Type: application/json` with body `{"filename": "...", "content_type": "...", "total_size": ...}` + + **Complete Resumable Session:** + Complete a session after all parts are uploaded. + Requires: `X-Upload-Session` header with session ID and `X-Upload-Complete: true` header. + + **Resumable Session Ownership:** + A session created with a user access token remains bound to that user. A session + created with an anon key remains bound to that exact anon key. Reuse the same + identity or anon key for part uploads, status, completion, and abort requests; + an ownership mismatch returns `404`. + operationId: uploadStorageObject + security: + - AnonKey: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/BucketName' + - name: path + in: path + required: true + description: Object path within bucket + schema: + type: string + - name: X-Upload-Session + in: header + required: false + description: Upload session ID (for completing resumable uploads) + schema: + type: string + - name: X-Upload-Complete + in: header + required: false + description: Set to "true" to complete a resumable upload session + schema: + type: string + enum: + - 'true' + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: + - file + properties: + file: + type: string + format: binary + description: File to upload (simple upload) + application/json: + schema: + $ref: '#/components/schemas/CreateUploadSessionRequest' + responses: + '200': + description: Resumable upload completed (when X-Upload-Complete=true) + content: + application/json: + schema: + $ref: '#/components/schemas/CompleteUploadSessionResponse' + '201': + description: File uploaded or session created + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/StorageObject' + - $ref: '#/components/schemas/CreateUploadSessionResponse' + '400': + description: | + Bad request. This can occur when: + - MIME type is not in the bucket's allowed_mime_types list + - File exceeds the bucket's configured file_size_limit + - File exceeds the global maximum upload size (5GB) + - Invalid request body or missing required fields + '403': + description: Access denied by storage policy + '404': + description: Resumable upload session not found or not owned by this credential + '413': + description: | + File size exceeds plan-based limits. This occurs when: + - File exceeds the plan-based maximum file size (FREE or PRO tier) + - The account on the FREE plan holds its file-storage allowance. + Enforcement is eventual: uploads are accepted until Volcano's + next allowance check sees the account at its allowance + '429': + $ref: '#/components/responses/BandwidthCapExceeded' + put: + tags: + - Storage Objects + summary: Upload a part of a resumable upload + description: | + Upload a single part of a resumable upload session. + + **Requirements:** + - Part numbers start at 1 + - All parts except the last must be at least 5MB + - Maximum part size is 25MB + - Parts can be uploaded in any order + - Re-uploading a part overwrites the previous upload + - Anonymous sessions must reuse the exact anon key that created the session + operationId: uploadPart + security: + - AnonKey: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/BucketName' + - name: path + in: path + required: true + description: Object path within bucket + schema: + type: string + - name: X-Upload-Session + in: header + required: true + description: Upload session ID + schema: + type: string + - name: X-Part-Number + in: header + required: true + description: Part number (1 to 10000) + schema: + type: integer + minimum: 1 + maximum: 10000 + requestBody: + required: true + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + '200': + description: Part uploaded + content: + application/json: + schema: + $ref: '#/components/schemas/UploadSessionPart' + '400': + description: Invalid part number or part data + '403': + description: Access denied + '404': + description: Session not found or not owned by this credential + get: + tags: + - Storage Objects + summary: Download a file or get upload session status + description: | + Download a file, or get the status of a resumable upload session. + + **File Download (default):** + Downloads the file at the specified path. + + **Session Status (with X-Upload-Session header):** + Returns the status of a resumable upload session, including which parts have been uploaded. + Anonymous sessions must reuse the exact anon key that created the session. + operationId: downloadStorageObject + security: + - AnonKey: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/BucketName' + - name: path + in: path + required: true + description: Object path within bucket + schema: + type: string + - name: Range + in: header + required: false + description: | + HTTP Range header for partial downloads. + Format: bytes=start-end or bytes=start- + Examples: bytes=0-1023, bytes=1000- + schema: + type: string + pattern: ^bytes=\d+-\d*$ + - name: X-Upload-Session + in: header + required: false + description: Upload session ID (to get session status instead of downloading) + schema: + type: string + responses: + '200': + description: File content or session status + headers: + Content-Type: + schema: + type: string + Content-Length: + schema: + type: integer + ETag: + schema: + type: string + content: + application/octet-stream: + schema: + type: string + format: binary + application/json: + schema: + $ref: '#/components/schemas/UploadSessionStatusResponse' + '206': + description: Partial content (range request) + '400': + description: Invalid Range header format + '403': + description: Access denied by storage policy + '404': + description: Object or session not found, or session not owned by this credential + '429': + $ref: '#/components/responses/BandwidthCapExceeded' + delete: + tags: + - Storage Objects + summary: Delete a file or abort upload session + description: | + Delete a file, or abort a resumable upload session. + + **File Delete (default):** + Deletes the file at the specified path. + + **Abort Session (with X-Upload-Session header):** + Aborts a resumable upload session and cleans up any uploaded parts. + Anonymous sessions must reuse the exact anon key that created the session. + operationId: deleteStorageObject + security: + - AnonKey: [] + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/BucketName' + - name: path + in: path + required: true + description: Object path within bucket + schema: + type: string + - name: X-Upload-Session + in: header + required: false + description: Upload session ID (to abort session instead of deleting file) + schema: + type: string + responses: + '200': + description: Object deleted or session aborted + '403': + description: Access denied by storage policy + '404': + description: Object or session not found, or session not owned by this credential + '429': + $ref: '#/components/responses/BandwidthCapExceeded' + /storage/{bucketName}/{path}/visibility: + patch: + tags: + - Storage Objects + summary: Update file visibility (public/private) + description: | + Change whether a file is publicly accessible. Only the file owner or a service key can change visibility. + If the bucket defines UPDATE policies, the owner must also satisfy one of them. + + - Public files can be downloaded with just an anon key (no user authentication required) + - Private files (default) require authentication and must pass policy checks + - All downloads go through the Volcano API - there is no direct access to the underlying store + operationId: updateStorageObjectVisibility + security: + - ServiceRoleKey: [] + - AuthUserAccessToken: [] + parameters: + - $ref: '#/components/parameters/BucketName' + - name: path + in: path + required: true + description: Object path within bucket + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/StorageVisibilityRequest' + responses: + '200': + description: Visibility updated + content: + application/json: + schema: + $ref: '#/components/schemas/StorageObject' + '403': + description: Not the file owner or denied by the bucket's UPDATE policies + '404': + description: Object not found + /public/{projectId}/{bucketName}/{path}: + get: + tags: + - Storage Objects + summary: Download a public file (no authentication required) + description: | + Download a file that has been marked as public. This endpoint requires NO authentication. + + **Access Requirements:** + - The file must have `is_public: true` set via the visibility endpoint + - Private files will return 403 Forbidden + + **Use Cases:** + - Shareable public URLs for profile pictures, public documents, etc. + - Embedding public files on external websites + - Direct linking without requiring SDK or authentication + + **URL Format:** + ``` + GET /public/{projectId}/{bucketName}/{path} + ``` + + **Example:** + ``` + https://api.volcano.dev/public/abc123/avatars/user-photo.jpg + ``` + + **CORS:** + This endpoint allows all origins since the file is already public. + operationId: downloadPublicFile + parameters: + - name: projectId + in: path + required: true + description: Project ID + schema: + type: string + format: uuid + - $ref: '#/components/parameters/BucketName' + - name: path + in: path + required: true + description: Object path within bucket + schema: + type: string + responses: + '200': + description: File content + content: + '*/*': + schema: + type: string + format: binary + '206': + description: Partial content (range request) + '404': + description: Not found (file doesn't exist or is not public) + '429': + $ref: '#/components/responses/BandwidthCapExceeded' + /health: + get: + tags: + - System + summary: Health check endpoint + description: Returns server health status. Used for load balancer and monitoring checks. + operationId: healthCheck + responses: + '200': + description: Server is healthy + content: + text/plain: + schema: + type: string + example: OK + /openapi.json: + get: + tags: + - System + summary: Fetch the OpenAPI specification as JSON + description: | + Returns this specification as a self-contained JSON document, with every + reference resolved. It is generated from the same document the server + validates requests against, so a client generated from it cannot + describe a different API than the one that answers. + + No credential is required: a client generator fetches this by URL before + its user has a token, and every path here is already published in the + API reference. + + The response carries a strong `ETag`; send it back as `If-None-Match` to + get `304 Not Modified` instead of the whole document. + operationId: getOpenAPISpecJSON + security: [] + parameters: + - $ref: '#/components/parameters/IfNoneMatch' + responses: + '200': + description: The OpenAPI specification + headers: + ETag: + $ref: '#/components/headers/ETag' + content: + application/json: + schema: + $ref: '#/components/schemas/OpenAPISpecDocument' + '304': + $ref: '#/components/responses/OpenAPISpecNotModified' + '429': + $ref: '#/components/responses/OpenAPISpecThrottled' + head: + tags: + - System + summary: Check the JSON OpenAPI specification + description: | + The headers `GET /openapi.json` would return, so a cache can pick up the + current `ETag` without transferring the document. + operationId: headOpenAPISpecJSON + security: [] + parameters: + - $ref: '#/components/parameters/IfNoneMatch' + responses: + '200': + $ref: '#/components/responses/OpenAPISpecHeaders' + '304': + $ref: '#/components/responses/OpenAPISpecNotModified' + '429': + $ref: '#/components/responses/OpenAPISpecThrottled' + /openapi.yaml: + get: + tags: + - System + summary: Fetch the OpenAPI specification as YAML + description: | + The same document as `/openapi.json`, serialized as YAML for tools that + prefer it. See that operation for caching and authentication notes. + operationId: getOpenAPISpecYAML + security: [] + parameters: + - $ref: '#/components/parameters/IfNoneMatch' + responses: + '200': + description: The OpenAPI specification + headers: + ETag: + $ref: '#/components/headers/ETag' + content: + application/yaml: + schema: + $ref: '#/components/schemas/OpenAPISpecDocument' + '304': + $ref: '#/components/responses/OpenAPISpecNotModified' + '429': + $ref: '#/components/responses/OpenAPISpecThrottled' + head: + tags: + - System + summary: Check the YAML OpenAPI specification + description: | + The headers `GET /openapi.yaml` would return, so a cache can pick up the + current `ETag` without transferring the document. + operationId: headOpenAPISpecYAML + security: [] + parameters: + - $ref: '#/components/parameters/IfNoneMatch' + responses: + '200': + $ref: '#/components/responses/OpenAPISpecHeaders' + '304': + $ref: '#/components/responses/OpenAPISpecNotModified' + '429': + $ref: '#/components/responses/OpenAPISpecThrottled' + /mcp: + post: + tags: + - System + summary: Model Context Protocol endpoint + description: | + Streamable-HTTP MCP endpoint: one JSON-RPC 2.0 object per request, one + response per request. There is no server-to-client stream, so a `GET` + returns `405`, and a batched array is rejected. + + Authenticated with a **project access token**. The endpoint takes its + project from the credential, so a platform token is refused with `403` — + it names no project, and letting a tool argument choose one would hand an + agent its own blast radius. + + Scope carries over from the REST API. A `read_only` token is not offered + mutating tools or credential-returning reads, and is refused if it calls + one anyway. Revoking the token ends MCP access on the same path it ends + API access. + + Methods: `initialize`, `notifications/initialized`, `ping`, + `tools/list`, `tools/call`. See the + [MCP guide](https://docs.volcano.dev/platform/interfaces/mcp) for the + tool surface and client configuration. + operationId: callMCP + security: + - ProjectAccessToken: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + description: A JSON-RPC 2.0 request object. + required: + - jsonrpc + - method + properties: + jsonrpc: + type: string + enum: + - '2.0' + method: + type: string + description: The MCP method to call. + id: + description: | + Request identifier, echoed verbatim. Omit it to send a + notification, which is answered with `202` and no body. + oneOf: + - type: string + - type: integer + params: + type: object + additionalProperties: true + responses: + '200': + description: A JSON-RPC response object + content: + application/json: + schema: + type: object + required: + - jsonrpc + - id + properties: + jsonrpc: + type: string + enum: + - '2.0' + id: + description: Echoes the request's id. Null when the request could not be read well enough to determine one. + nullable: true + oneOf: + - type: string + - type: integer + result: + type: object + additionalProperties: true + error: + type: object + required: + - code + - message + properties: + code: + type: integer + message: + type: string + '202': + description: A notification was accepted; there is no body + '401': + description: Not authenticated + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: | + The credential is valid but may not use this endpoint — most often a + platform token, which names no project. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '413': + description: The JSON-RPC frame exceeds the 1 MiB limit +components: + headers: + ETag: + description: | + Strong validator for the returned document. Send it back as `If-None-Match` + to revalidate without transferring the document again. + required: true + schema: + type: string + example: '"9f2c1e0b5a"' + DatabaseQueryProxyMs: + description: | + Milliseconds Volcano spent before running the query, counted from the + request arriving: authenticating the caller, validating the request, + resolving the database, and building the SQL. Does not include query + execution. Also present when the query itself failed. + schema: + type: integer + minimum: 0 + DatabaseQueryProxyHandlerMs: + description: | + The part of `X-Volcano-Proxy-Ms` spent in the query endpoint itself. + Subtract it from `X-Volcano-Proxy-Ms` to see what authentication and + request validation cost. Also present when the query itself failed. + schema: + type: integer + minimum: 0 + DatabaseQueryComputeMs: + description: | + Milliseconds the database took to run the query and return its rows. + Does not include Volcano's preparation. Also present when the query + itself failed. + schema: + type: integer + minimum: 0 + securitySchemes: + AnonKey: + type: http + scheme: bearer + bearerFormat: JWT + description: | + Project-specific public key for frontend authentication. + Required for signup, signin, refresh, and logout endpoints. + Get from Project Settings → Authentication → Anon Keys. + Safe to expose in frontend code (scoped to project, limited permissions). + AuthUserAccessToken: + type: http + scheme: bearer + bearerFormat: JWT + description: | + Auth user access token obtained from signup/signin. + Used for authenticated function invocation and user profile access. + Functions invoked with access tokens receive user context in event.__volcano_auth. + Expires after configured lifetime (default: 1 hour). + ServiceRoleKey: + type: http + scheme: bearer + bearerFormat: JWT + description: | + Service role key for admin operations. + **WARNING:** Bypasses Row-Level Security - backend use only! + Create via POST /projects/{id}/service-keys. + Used for function invocation with full database access. + UserToken: + type: http + scheme: bearer + bearerFormat: opaque + description: | + Platform token, which acts on every project in your account. + Obtain one by running `volcano login`, or from the dashboard. + + For CI, scripts, and agents, prefer a project access token + (`pt-`) instead: it reaches only the project it was created in, + so a leak does not expose the rest of your account. See the + ProjectAccessToken scheme. + ProjectAccessToken: + type: http + scheme: bearer + bearerFormat: opaque + description: | + Project access token, scoped to a single project. Starts with `pt-`. + + Accepted on that project's control-plane routes and refused + everywhere else, including account-wide endpoints and any other + project. A `read_only` token additionally refuses mutations. + + Create one with POST /projects/{id}/access-tokens using a platform + token. The secret is returned once and is not recoverable, so a + project access token cannot create, list, read, or revoke project + access tokens. + parameters: + BackupName: + name: backupName + in: path + required: true + schema: + type: string + minLength: 1 + maxLength: 128 + description: | + Backup name, unique within the database, exactly as returned by the list + endpoint. + + Deliberately looser than the names you can create: a backup made by a + schedule is named for you, so reading or deleting one accepts any name a + backup can have. + BranchName: + name: branchName + in: path + required: true + schema: + type: string + pattern: ^[a-z0-9_]+$ + maxLength: 64 + description: Branch name (unique within the parent database, lowercase letters, numbers, and underscores only) + BucketName: + name: bucketName + in: path + required: true + schema: + type: string + pattern: ^[a-zA-Z0-9_-]+$ + minLength: 1 + maxLength: 64 + description: Storage bucket name + Cursor: + name: cursor + in: query + required: false + schema: + type: string + description: | + Opaque keyset pagination cursor from a previous response's `next_cursor` + — pages forward. Mutually exclusive with `page` and `ending_before`; + combining them returns 400. When supplied, the request's `search` and + `limit` must match the values bound to the cursor or the request returns 400. + EndingBefore: + name: ending_before + in: query + required: false + schema: + type: string + description: | + Opaque keyset pagination cursor from a previous response's `prev_cursor` + — pages backward (the page immediately preceding this cursor). Mutually + exclusive with `page` and `cursor`; combining them returns 400. `search` + and `limit` must match the values bound to the cursor or the request + returns 400. + DatabaseName: + name: databaseName + in: path + required: true + schema: + type: string + pattern: ^[a-z0-9_]+$ + maxLength: 64 + description: Database name (unique within project, lowercase letters, numbers, and underscores only) + DeploymentId: + name: deploymentId + in: path + required: true + schema: + type: string + format: uuid + description: Frontend deployment ID + FrontendId: + name: frontendId + in: path + required: true + schema: + type: string + format: uuid + description: Frontend ID + FrontendFunctionRouteId: + name: routeId + in: path + required: true + schema: + type: string + format: uuid + description: Frontend Function route ID + FunctionId: + name: functionId + in: path + required: true + schema: + type: string + format: uuid + description: Function ID + DurableFunctionId: + name: functionId + in: path + required: true + schema: + type: string + description: Durable function ID, or its name within the project + DurableExecutionId: + name: executionId + in: path + required: true + schema: + type: string + format: uuid + description: Durable execution ID + Limit: + name: limit + in: query + required: false + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + description: Number of items per page (max 100) + LockKey: + name: key + in: path + required: true + description: Project-local lock name. + schema: + type: string + minLength: 1 + maxLength: 128 + pattern: ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$ + LockToken: + name: X-Volcano-Lock-Token + in: header + required: true + description: Opaque UUID generated once by the caller and retained for the lease lifetime. + schema: + type: string + format: uuid + LockRequestId: + name: X-Volcano-Request-Id + in: header + required: true + description: | + UUID correlating this request across client and server logs. Repeat safety comes from + the lock token, so a retry under a reused request ID still counts against the quota. + schema: + type: string + format: uuid + DeploymentOperation: + name: operation + in: query + required: false + description: Restrict a deployment feed to one kind of operation. + schema: + type: string + enum: + - deploy + - redeploy + - update + - delete + DeploymentOwnerId: + name: owner_id + in: query + required: false + description: | + The user who owns the projects whose deployments to return + (`projects.user_id`). This is ownership, not the actor that started the + deployment — see `initiated_by_user_id` for that. Not a UUID: platform + user ids are opaque strings. + schema: + type: string + maxLength: 255 + DeploymentOrder: + name: order + in: query + required: false + description: | + Sort key and direction. `created_at.desc` (default) is the feed order. + `completed_at.asc` orders finished attempts by completion, oldest first, + and excludes attempts that never completed. + schema: + type: string + enum: + - created_at.desc + - completed_at.asc + default: created_at.desc + DeploymentResourceType: + name: resource_type + in: query + required: false + description: | + Restrict a deployment feed to a single resource type. Omit to return + both Function and Frontend deployments. + schema: + type: string + enum: + - function + - frontend + DeploymentStatus: + name: status + in: query + required: false + description: Restrict a deployment feed to attempts in one status. + schema: + type: string + enum: + - queued + - provisioning + - active + - degraded + - failed + - superseded + - deleting + - deleted + Offset: + name: offset + in: query + required: false + schema: + type: integer + minimum: 0 + default: 0 + description: | + Bounded row offset past the keyset anchor named by `cursor` (forward) or + `ending_before` (backward) — the hybrid jump. Seek to the anchor, then + skip this many rows within. Used for numbered jump-to-page: from the + current page, seek to its next/prev cursor and offset the remaining + pages. Only honored on the cursor pagination path; ignored otherwise. + Page: + name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: | + Page number (1-indexed) for offset pagination. Declares no schema + default so the request validator does not inject one: handlers that omit + `page` see it unset (nil) and default to 1 in code, while cursor-first + endpoints (e.g. the project deployments feed) can detect its absence to + stay in keyset/search mode. Supplying `page` selects offset pagination. + ProjectId: + name: id + in: path + required: true + schema: + type: string + format: uuid + description: Project ID + TokenId: + name: tokenId + in: path + required: true + schema: + type: string + format: uuid + description: Project access token ID + Search: + name: search + in: query + required: false + schema: + type: string + maxLength: 256 + description: | + Case-insensitive substring match on the resource `name`. See the + endpoint description for supported pagination modes. + RestoreId: + name: restoreId + in: path + required: true + schema: + type: string + format: uuid + description: Database restore ID + SchedulerId: + name: schedulerId + in: path + required: true + schema: + type: string + format: uuid + description: Function scheduler ID + VariableName: + name: name + in: path + required: true + schema: + type: string + minLength: 1 + maxLength: 256 + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + description: Variable name + IfNoneMatch: + name: If-None-Match + in: header + required: false + description: | + Entity tag from an earlier response, returning `304 Not Modified` while it + still matches. Accepts the full condition: `*`, a comma-separated list, and + weak tags of the form `W/"tag"`. + schema: + type: string + example: '"9f2c1e0b5a"' + responses: + OpenAPISpecThrottled: + description: | + Too many requests for the specification from one address. The document + carries an `ETag`; revalidate with `If-None-Match` rather than + re-fetching it. + headers: + Retry-After: + description: Seconds to wait before requesting the specification again. + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + BandwidthCapExceeded: + description: | + The platform user exceeded their billing-cycle bandwidth allowance (aggregate + ingress + egress across owned projects). Enforcement is eventual: + requests are rejected until the allowance increases or the next + anniversary cycle begins. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + DatabaseQueryCapExceeded: + description: | + The query was rejected by a billing-cycle allowance: either the owning + platform user's bandwidth allowance (aggregate ingress + egress across + owned projects) or their database-request allowance. Enforcement is + eventual: queries are rejected until the allowance increases or the + next anniversary cycle begins. The error message identifies the resource. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + DatabaseBranchQueryUnavailable: + description: | + The branch exists but cannot serve queries: it is still provisioning, + being reset, expired, or its parent is being restored. Distinct from + `404` so a caller waiting on a branch can tell it apart from a typo. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + OpenAPISpecNotModified: + description: | + The specification still matches the supplied `If-None-Match`, so no body + is returned. + headers: + ETag: + $ref: '#/components/headers/ETag' + OpenAPISpecHeaders: + description: | + The headers a `GET` would return, without the document. The declared + `ETag` is the one to revalidate against. + headers: + ETag: + $ref: '#/components/headers/ETag' + schemas: + SandboxPreset: + type: object + additionalProperties: false + properties: + id: + type: string + runtime: + type: string + version: + type: string + memory_mb: + type: integer + enum: + - 1024 + - 2048 + regions: + type: array + items: + type: string + required: + - id + - runtime + - version + - memory_mb + - regions + SandboxTemplate: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + project_id: + type: string + format: uuid + name: + type: string + pattern: ^[a-z][a-z0-9-]{0,62}$ + preset: + type: string + memory_mb: + type: integer + status: + type: string + enum: + - ready + - unavailable + - deleting + created_at: + type: string + format: date-time + required: + - id + - project_id + - name + - status + - created_at + CreateSandboxTemplateRequest: + type: object + additionalProperties: false + properties: + name: + type: string + pattern: ^[a-z][a-z0-9-]{0,62}$ + preset: + type: string + enum: + - python3.12 + - node22 + memory_mb: + type: integer + enum: + - 1024 + - 2048 + default: 1024 + required: + - name + - preset + UpdateSandboxTemplateRequest: + type: object + additionalProperties: false + properties: + name: + type: string + pattern: ^[a-z][a-z0-9-]{0,62}$ + required: + - name + SandboxSession: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + project_id: + type: string + format: uuid + sandbox_id: + type: string + format: uuid + state: + type: string + enum: + - starting + - running + - suspending + - suspended + - resuming + - terminating + - terminated + - unknown + desired_state: + type: string + enum: + - running + - suspended + - terminated + region: + type: string + memory_mb: + type: integer + created_at: + type: string + format: date-time + started_at: + type: string + format: date-time + expires_at: + type: string + format: date-time + required: + - id + - project_id + - sandbox_id + - state + - desired_state + - region + - memory_mb + - created_at + - expires_at + CreateSandboxSessionRequest: + type: object + additionalProperties: false + properties: + preset: + type: string + enum: + - python3.12 + - node22 + sandbox_id: + type: string + format: uuid + memory_mb: + type: integer + enum: + - 1024 + - 2048 + region: + type: string + pattern: ^aws-[a-z0-9-]+$ + max_duration_seconds: + type: integer + minimum: 30 + maximum: 28800 + default: 3600 + idle_timeout_seconds: + type: integer + minimum: 0 + maximum: 28800 + default: 0 + required: + - region + oneOf: + - required: + - preset + not: + required: + - sandbox_id + - required: + - sandbox_id + not: + required: + - preset + SandboxCommandRequest: + type: object + additionalProperties: false + properties: + command: + type: string + minLength: 1 + maxLength: 65536 + timeout_seconds: + type: integer + minimum: 1 + maximum: 3600 + default: 60 + environment: + type: object + additionalProperties: + type: string + maxProperties: 64 + required: + - command + SandboxExecutionRequest: + type: object + additionalProperties: false + properties: + preset: + type: string + enum: + - python3.12 + - node22 + sandbox_id: + type: string + format: uuid + memory_mb: + type: integer + enum: + - 1024 + - 2048 + region: + type: string + pattern: ^aws-[a-z0-9-]+$ + command: + type: string + minLength: 1 + maxLength: 65536 + timeout_seconds: + type: integer + minimum: 1 + maximum: 60 + default: 60 + environment: + type: object + additionalProperties: + type: string + maxProperties: 64 + required: + - region + - command + oneOf: + - required: + - preset + not: + required: + - sandbox_id + - required: + - sandbox_id + not: + required: + - preset + SandboxCommandResult: + type: object + additionalProperties: false + properties: + stdout: + type: string + stderr: + type: string + exit_code: + type: integer + stdout_truncated: + type: boolean + stderr_truncated: + type: boolean + timed_out: + type: boolean + required: + - stdout + - stderr + - exit_code + - stdout_truncated + - stderr_truncated + - timed_out + SandboxExecutionResult: + type: object + additionalProperties: false + properties: + stdout: + type: string + stderr: + type: string + exit_code: + type: integer + stdout_truncated: + type: boolean + stderr_truncated: + type: boolean + timed_out: + type: boolean + session_id: + type: string + format: uuid + region: + type: string + duration_ms: + type: integer + format: int64 + minimum: 0 + required: + - stdout + - stderr + - exit_code + - stdout_truncated + - stderr_truncated + - timed_out + - session_id + - region + - duration_ms + SandboxFileWriteRequest: + type: object + additionalProperties: false + properties: + path: + type: string + minLength: 1 + maxLength: 4096 + data: + type: string + format: byte + maxLength: 11184812 + required: + - path + - data + SandboxFileReadRequest: + type: object + additionalProperties: false + properties: + path: + type: string + minLength: 1 + maxLength: 4096 + required: + - path + SandboxFileResult: + type: object + additionalProperties: false + properties: + data: + type: string + format: byte + required: + - data + SandboxSubjectGrantRequest: + type: object + additionalProperties: false + properties: + expires_at: + type: string + format: date-time + required: + - expires_at + SandboxAccessRequest: + type: object + additionalProperties: false + properties: + port: + type: integer + minimum: 1 + maximum: 65532 + expires_in_seconds: + type: integer + minimum: 1 + maximum: 300 + default: 300 + required: + - port + SandboxAccess: + type: object + additionalProperties: false + properties: + url: + type: string + format: uri + token: + type: string + expires_at: + type: string + format: date-time + required: + - url + - token + - expires_at + SandboxDeployment: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + status: + type: string + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + required: + - id + - status + - created_at + - updated_at + SandboxPagination: + type: object + additionalProperties: false + properties: + limit: + type: integer + has_more: + type: boolean + next_cursor: + type: string + required: + - limit + - has_more + SandboxTemplatePage: + type: object + additionalProperties: false + properties: + data: + type: array + items: + $ref: '#/components/schemas/SandboxTemplate' + pagination: + $ref: '#/components/schemas/SandboxPagination' + required: + - data + - pagination + SandboxSessionPage: + type: object + additionalProperties: false + properties: + data: + type: array + items: + $ref: '#/components/schemas/SandboxSession' + pagination: + $ref: '#/components/schemas/SandboxPagination' + required: + - data + - pagination + SandboxDeploymentPage: + type: object + additionalProperties: false + properties: + data: + type: array + items: + $ref: '#/components/schemas/SandboxDeployment' + pagination: + $ref: '#/components/schemas/SandboxPagination' + required: + - data + - pagination + SandboxPresetList: + type: object + additionalProperties: false + properties: + data: + type: array + items: + $ref: '#/components/schemas/SandboxPreset' + required: + - data + SandboxCapacity: + type: object + additionalProperties: false + properties: + region: + type: string + allocated_memory_mb: + type: integer + format: int64 + minimum: 0 + required: + - region + - allocated_memory_mb + SandboxCapacityList: + type: object + additionalProperties: false + properties: + data: + type: array + items: + $ref: '#/components/schemas/SandboxCapacity' + required: + - data + PublishSandboxPresetRequest: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + preset: + type: string + enum: + - python3.12 + - node22 + memory_mb: + type: integer + enum: + - 1024 + - 2048 + deployment_id: + type: string + format: uuid + required: + - id + - preset + - memory_mb + - deployment_id AnonKey: type: object properties: @@ -15146,6 +17560,20 @@ components: Frontend: type: object properties: + variable_scope: + type: string + enum: + - all + - shared + - scoped + description: All preserves access to all project variables. Shared includes the project frontend shared-variable list. Scoped includes only explicitly declared variables in builds and runtime. Omission preserves the stored selection. + declared_variables: + type: array + uniqueItems: true + items: + type: string + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + description: Names selected when variable_scope is scoped. Missing declared values reject deployment. Omission preserves the stored list; an empty list clears it. id: type: string format: uuid @@ -15229,6 +17657,72 @@ components: - deployed_regions - created_at - updated_at + FrontendFunctionRoute: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + project_id: + type: string + format: uuid + frontend_id: + type: string + format: uuid + function_id: + type: string + format: uuid + path_prefix: + type: string + minLength: 2 + maxLength: 512 + pattern: ^/[^?#\\]*[^/?#\\]$ + strip_prefix: + type: boolean + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + required: + - id + - project_id + - frontend_id + - function_id + - path_prefix + - strip_prefix + - created_at + - updated_at + FrontendFunctionRouteList: + type: object + additionalProperties: false + properties: + data: + type: array + items: + $ref: '#/components/schemas/FrontendFunctionRoute' + required: + - data + CreateFrontendFunctionRouteRequest: + type: object + additionalProperties: false + properties: + function_id: + type: string + format: uuid + path_prefix: + type: string + minLength: 2 + maxLength: 512 + pattern: ^/[^?#\\]*[^/?#\\]$ + strip_prefix: + type: boolean + default: false + required: + - function_id + - path_prefix FrontendCustomDomainResponse: type: object properties: @@ -15488,6 +17982,217 @@ components: - total_requests - total_errors - total_page_views + ProjectAccessToken: + type: object + description: | + A project access token: a control-plane credential bound to a single + project. Unlike a platform token, which acts on every project its owner + has, this one is limited to the project it was created in. + + The secret itself is never returned here. Only its hash is stored, so + the plaintext exists solely in the response to the create call. + properties: + id: + type: string + format: uuid + project_id: + type: string + format: uuid + name: + type: string + description: Unique per project. + token_prefix: + type: string + description: First 12 characters of the secret, for recognising a token in a list. + scope: + $ref: '#/components/schemas/ProjectAccessTokenScope' + status: + type: string + enum: + - active + - revoked + - expired + description: | + `revoked` means the token was deliberately revoked, by you or by the + deletion of its project. `expired` means it simply reached + `expires_at`; nothing was taken away. Both are refused, and both keep + their record so a token's name, prefix, last use, and request history + remain available after a leak. + + A token revoked before its expiry passed stays `revoked`, because + that is the fact worth keeping. + token_source: + type: string + enum: + - api + - cli + - dashboard + description: What created the token. + expires_at: + type: string + format: date-time + nullable: true + description: Absent for a token that does not expire. + last_used_at: + type: string + format: date-time + nullable: true + description: Updated at most once every few minutes, so it may lag slightly. + created_at: + type: string + format: date-time + all_time_requests: + type: integer + format: int64 + description: Requests authenticated with this token since it was created. + required: + - id + - project_id + - name + - token_prefix + - scope + - status + - token_source + - created_at + - all_time_requests + ProjectAccessTokenScope: + type: string + description: | + What a project access token may do within its project. + + `full` is everything you can do to that one project, up to and including + deleting it. It cannot manage access tokens, so a leaked token cannot + mint a replacement or erase the record of its own use, but for a CI or + agent credential that only deploys, prefer `read_only` where the job + allows it. + + `read_only` refuses mutations. It is enforced by route classification + rather than HTTP method, so the log and metrics query endpoints remain + available even though they are POST requests that carry body filters. + + `read_only` also refuses the reads that return a credential — service + keys, anon keys, variable values, and database connection strings. Those + grant write access over the project's data and keep working after the + token that fetched them is revoked, so returning one to a read-only + credential would make the scope a formality. An anon key is included + because its permissions are chosen per key and may include uploading, + deleting, and publishing. + enum: + - full + - read_only + x-enum-varnames: + - ProjectAccessTokenScopeFull + - ProjectAccessTokenScopeReadOnly + CreateProjectAccessTokenRequest: + type: object + properties: + name: + type: string + minLength: 1 + maxLength: 100 + description: | + Held by any of the project's tokens you have not revoked, including + one that has expired. Creating a duplicate returns 409 with code + `access_token_name_exists`; revoking the holder frees the name, so a + rotation can keep the name its caller already references. + scope: + $ref: '#/components/schemas/ProjectAccessTokenScope' + expires_at: + type: string + format: date-time + description: Omit for a token that does not expire. + required: + - name + - scope + CreatedProjectAccessToken: + allOf: + - $ref: '#/components/schemas/ProjectAccessToken' + - type: object + properties: + token: + type: string + description: | + The secret. Returned only here, and not recoverable afterwards: + the server stores a hash rather than the value. Save it now. + required: + - token + PaginatedProjectAccessTokens: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/ProjectAccessToken' + page: + type: integer + limit: + type: integer + total: + type: integer + has_more: + type: boolean + next: + type: string + required: + - data + - page + - limit + - total + - has_more + ProjectAccessTokenUsage: + type: object + description: | + Zero-filled daily request counts for a single token, oldest first. Every + day in the window is present, so a gap reads as zero rather than missing. + + Counts every request the token authenticated, including ones then + refused — a read-only token attempting a write, or a token presented on + another project's route. That is deliberate: after a leak, the probing + is the part you want to see, and a counter that hid it would make a + token look idle while it was being tried. + properties: + token_id: + type: string + format: uuid + name: + type: string + token_prefix: + type: string + description: | + The token's display prefix, which identifies the credential when its + name does not. Revoking frees a name, so a project that rotated + `ci-deploy` has two entries here both called `ci-deploy`. Not usable + as a credential. + days: + type: integer + description: Number of daily entries returned, always equal to the requested window. + daily: + type: array + items: + $ref: '#/components/schemas/ProjectAccessTokenUsageDailyEntry' + total_requests: + type: integer + format: int64 + required: + - token_id + - name + - token_prefix + - days + - daily + - total_requests + ProjectAccessTokenUsageDailyEntry: + type: object + properties: + day: + type: string + format: date + description: UTC day. + requests: + type: integer + format: int64 + required: + - day + - requests Function: type: object properties: @@ -15534,7 +18239,7 @@ components: type: string invoke_url: type: string - description: Canonical GeoDNS endpoint URL for invoking this function (always HTTPS) + description: 'Canonical geo-routed HTTPS endpoint for invoking this function. Use it as-is: it does not share a domain with the API, so a host derived from the API URL will not reach the function. Omitted when the deployment serves no public invocation domain, as in local development, so a client testing for an empty string never matches.' deployed_regions: type: array items: @@ -15761,12 +18466,18 @@ components: `succeeded`, `failed`, `timed_out`, `stopped` and `unknown` are terminal. - `unknown` means the platform lost track of the execution's outcome: it - was never seen to finish and is no longer reported, so no result or - error can be given for it. It is terminal because nothing can settle it - later, and it is rare — treat it as an outcome to retry under a new - name rather than a state to wait on. `completed_at` on an `unknown` - execution is when the platform gave up, not when the work ended. + `unknown` means the execution's outcome cannot be established, so no + result or error can be given for it. Either it was under way and was + never seen to finish, or its start failed with a `500` without the + platform establishing whether the execution began — which is why a + name whose start returned an error can later read as `unknown` rather + than not being found. It is terminal because nothing can settle it + later, and it is rare — treat it as an outcome to retry rather than a + state to wait on. A retry under the same name picks this execution back + up instead of starting a second one, and needs a free concurrency slot + because an `unknown` execution has given its own up. `completed_at` on + an `unknown` execution is when the platform gave up, not when the work + ended. DurableExecutionError: type: object description: Why a failed or timed-out execution ended. @@ -16967,6 +19678,14 @@ components: items: type: string pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + frontend_shared_variables: + type: array + uniqueItems: true + description: Replace the complete shared frontend-variable list with existing names. Frontends with variable_scope shared receive this list. Omission keeps membership unchanged; an empty list clears it. + items: + type: string + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + maxLength: 256 variables: type: array description: Fully synced when declared - variables absent from this list are deleted. @@ -17455,11 +20174,31 @@ components: created or deleted through the manifest. A declared frontend entry without `custom_domain` deletes an existing custom domain. properties: + variable_scope: + type: string + enum: + - all + - shared + - scoped + description: All preserves access to all project variables. Shared includes the project frontend_shared_variables list. Scoped includes only explicitly declared variables in builds and runtime. Omission preserves the stored selection. + variables: + type: array + uniqueItems: true + items: + type: string + pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$ + description: Names selected when variable_scope is scoped. Missing declared values reject deployment. Omission preserves the stored list; an empty list clears it. name: type: string minLength: 1 custom_domain: $ref: '#/components/schemas/ProjectConfigCustomDomain' + function_routes: + type: array + maxItems: 64 + description: Complete set of same-origin Function path mappings when declared. Omission preserves existing mappings; an empty list deletes all mappings. + items: + $ref: '#/components/schemas/ProjectConfigFrontendFunctionRoute' required: - name ProjectConfigFunction: @@ -17486,6 +20225,9 @@ components: enum: - all - scoped + x-enum-varnames: + - ProjectConfigFunctionVariableScopeAll + - ProjectConfigFunctionVariableScopeScoped description: | Which project variables this function receives. `all` (the default) gives it the project variables marked `shared: true`. `scoped` gives it only the variables @@ -18297,6 +21039,9 @@ components: type: string format: uuid description: Canonical function ID used for invocation routing + invoke_url: + type: string + description: 'Canonical HTTPS endpoint for invoking this function. Use it as-is: it does not share a domain with the API, so a host derived from the API URL will not reach the function. Omitted when the deployment serves no public invocation domain, as in local development; invoke through POST /functions/{functionId}/invoke instead.' cache_ttl_seconds: type: integer minimum: 1 @@ -19077,6 +21822,9 @@ components: shared: type: boolean description: Include this name in the project's shared function variables. Omission preserves existing membership; new variables default to true for legacy clients. Send false explicitly to create a non-shared variable. + frontend_shared: + type: boolean + description: Whether this name is in the project's shared frontend-variable list. id: type: string format: uuid @@ -19126,6 +21874,12 @@ components: - value - created_at - updated_at + OpenAPISpecDocument: + type: object + additionalProperties: true + description: | + This OpenAPI document, with every reference resolved. Shared by the JSON + and YAML operations, which differ only in serialization. ProjectGitConnectionSummary: type: object properties: @@ -19219,6 +21973,25 @@ components: $ref: '#/components/schemas/AuthPageTheme' layouts: $ref: '#/components/schemas/ProjectConfigAuthPageLayouts' + ProjectConfigFrontendFunctionRoute: + type: object + additionalProperties: false + properties: + function: + type: string + minLength: 1 + description: Name of an existing standard Function configured for HTTP invocation. + path_prefix: + type: string + minLength: 2 + maxLength: 512 + pattern: ^/[^?#\\]*[^/?#\\]$ + strip_prefix: + type: boolean + default: false + required: + - function + - path_prefix DatabaseQueryPerformanceDatabase: type: object properties: diff --git a/sig/client.rbs b/sig/client.rbs index 435b4a7f..1b67b452 100644 --- a/sig/client.rbs +++ b/sig/client.rbs @@ -1,5 +1,6 @@ module Volcano class Client + attr_reader sandboxes: Sandboxes attr_reader auth: Auth attr_reader functions: Functions attr_reader durable: Durable diff --git a/sig/sandboxes.rbs b/sig/sandboxes.rbs new file mode 100644 index 00000000..45c93e9e --- /dev/null +++ b/sig/sandboxes.rbs @@ -0,0 +1,80 @@ +module Volcano + type sandbox_selector = { region: String, ?preset: String, ?sandbox_id: String, ?memory_mb: Integer } + type sandbox_create_options = { region: String, ?preset: String, ?sandbox_id: String, ?memory_mb: Integer, ?max_duration_seconds: Integer, ?idle_timeout_seconds: Integer, ?request_id: String } + type sandbox_exec_options = { region: String, ?preset: String, ?sandbox_id: String, ?memory_mb: Integer, ?timeout_seconds: Integer, ?environment: Hash[String, String], ?request_id: String } + type sandbox_command_options = { ?timeout_seconds: Integer, ?environment: Hash[String, String], ?request_id: String } + + class SandboxCommandResult < Data + attr_reader stdout: String + attr_reader stderr: String + attr_reader exit_code: Integer + attr_reader timed_out: bool + attr_reader stdout_truncated: bool + attr_reader stderr_truncated: bool + def self.new: (stdout: String, stderr: String, exit_code: Integer, timed_out: bool, stdout_truncated: bool, stderr_truncated: bool) -> SandboxCommandResult + def initialize: (stdout: String, stderr: String, exit_code: Integer, timed_out: bool, stdout_truncated: bool, stderr_truncated: bool) -> void + def to_h: () -> { stdout: String, stderr: String, exit_code: Integer, timed_out: bool, stdout_truncated: bool, stderr_truncated: bool } + end + + class SandboxExecutionResult < Data + attr_reader stdout: String + attr_reader stderr: String + attr_reader exit_code: Integer + attr_reader timed_out: bool + attr_reader stdout_truncated: bool + attr_reader stderr_truncated: bool + attr_reader session_id: String + attr_reader region: String + attr_reader duration_ms: Integer + def self.new: (stdout: String, stderr: String, exit_code: Integer, timed_out: bool, stdout_truncated: bool, stderr_truncated: bool, session_id: String, region: String, duration_ms: Integer) -> SandboxExecutionResult + def initialize: (stdout: String, stderr: String, exit_code: Integer, timed_out: bool, stdout_truncated: bool, stderr_truncated: bool, session_id: String, region: String, duration_ms: Integer) -> void + def to_h: () -> { stdout: String, stderr: String, exit_code: Integer, timed_out: bool, stdout_truncated: bool, stderr_truncated: bool, session_id: String, region: String, duration_ms: Integer } + end + + class SandboxPreset < Data + attr_reader id: String + attr_reader memory_mb: Integer + attr_reader regions: Array[String] + def self.new: (id: String, memory_mb: Integer, regions: Array[String]) -> SandboxPreset + def initialize: (id: String, memory_mb: Integer, regions: Array[String]) -> void + def to_h: () -> { id: String, memory_mb: Integer, regions: Array[String] } + end + + class SandboxAccess + attr_reader url: String + attr_reader token: String + attr_reader expires_at: String + def initialize: (url: String, token: String, expires_at: String) -> void + def inspect: () -> String + end + + class Sandboxes + def presets: () -> Array[SandboxPreset] + def create: (String project_id, sandbox_create_options options) -> SandboxSession + def get: (String session_id) -> SandboxSession + def exec: (String project_id, String command, sandbox_exec_options options) -> SandboxExecutionResult + def grant: (String session_id, String auth_user_id, expires_at: String) -> nil + def revoke: (String session_id, String auth_user_id) -> nil + end + + class SandboxSession + attr_reader id: String + attr_reader project_id: String + attr_reader region: String + attr_reader state: String + attr_reader expires_at: String + attr_reader files: SandboxFiles + def refresh: () -> SandboxSession + def suspend: () -> SandboxSession + def resume: () -> SandboxSession + def terminate: () -> SandboxSession + def exec: (String command, ?sandbox_command_options options) -> SandboxCommandResult + def access: (Integer port) -> SandboxAccess + def use: [T] () { (SandboxSession) -> T } -> T + end + + class SandboxFiles + def read: (String path) -> String + def write: (String path, String data) -> nil + end +end diff --git a/sig_client/client_runtime.rbs b/sig_client/client_runtime.rbs index 1f63b7c3..459616c4 100644 --- a/sig_client/client_runtime.rbs +++ b/sig_client/client_runtime.rbs @@ -1,5 +1,6 @@ module Volcano class Client + attr_reader sandboxes: Sandboxes attr_reader auth: Auth attr_reader functions: Functions attr_reader durable: Durable @@ -13,6 +14,7 @@ module Volcano @service_key: String? @auth_state: AuthState @transport: GeneratedTransport + @sandboxes: Sandboxes @auth: Auth @functions: Functions @durable: Durable diff --git a/sig_dev/generated_sandbox_boundary.rbs b/sig_dev/generated_sandbox_boundary.rbs new file mode 100644 index 00000000..34216977 --- /dev/null +++ b/sig_dev/generated_sandbox_boundary.rbs @@ -0,0 +1,26 @@ +module Volcano + class GeneratedTransport + SANDBOX_OPERATIONS: Hash[Symbol, ^(Generated::SandboxesApi, SandboxRequest) -> Generated::api_result] + def sandbox_request: (authorization: String, request: SandboxRequest) -> Transport::Response + private def sandbox_api: (String authorization, Integer timeout) -> Generated::SandboxesApi + end + + module Generated + class SandboxesApi + def initialize: (GeneratedTransport::ApiClient api_client) -> void + def list_sandbox_presets_with_http_info: (api_options options) -> api_result + def create_sandbox_session_with_http_info: (String?, String?, Hash[Symbol, json_value]?, api_options options) -> api_result + def execute_sandbox_with_http_info: (String?, String?, Hash[Symbol, json_value]?, api_options options) -> api_result + def get_sandbox_session_with_http_info: (String?, api_options options) -> api_result + def execute_sandbox_session_with_http_info: (String?, String?, Hash[Symbol, json_value]?, api_options options) -> api_result + def suspend_sandbox_session_with_http_info: (String?, api_options options) -> api_result + def resume_sandbox_session_with_http_info: (String?, api_options options) -> api_result + def terminate_sandbox_session_with_http_info: (String?, api_options options) -> api_result + def create_sandbox_session_access_with_http_info: (String?, Hash[Symbol, json_value]?, api_options options) -> api_result + def read_sandbox_session_file_with_http_info: (String?, Hash[Symbol, json_value]?, api_options options) -> api_result + def write_sandbox_session_file_with_http_info: (String?, Hash[Symbol, json_value]?, api_options options) -> api_result + def grant_sandbox_session_with_http_info: (String?, String?, Hash[Symbol, json_value]?, api_options options) -> api_result + def revoke_sandbox_session_with_http_info: (String?, String?, api_options options) -> api_result + end + end +end diff --git a/sig_dev/sandboxes.rbs b/sig_dev/sandboxes.rbs new file mode 100644 index 00000000..9cc0ad23 --- /dev/null +++ b/sig_dev/sandboxes.rbs @@ -0,0 +1,76 @@ +module Volcano + type sandbox_request_options = { operation: Symbol, ?resource_id: String, ?subject_id: String, ?body: Hash[Symbol, json_value], ?request_id: String, ?timeout: Integer } + + class SandboxRequest + @options: sandbox_request_options + attr_reader operation: Symbol + attr_reader resource_id: String? + attr_reader subject_id: String? + attr_reader body: Hash[Symbol, json_value]? + attr_reader request_id: String? + attr_reader timeout: Integer + def initialize: (sandbox_request_options options) -> void + end + + interface _SandboxTransport + def sandbox_request: (authorization: String, request: SandboxRequest) -> Transport::Response + end + + interface _SandboxClient + def current_session: () -> Session? + def service_token: () -> String + def session_request: () { (String) -> Transport::Response } -> Transport::Response + end + + class SandboxAccess + @values: Hash[Symbol, String] + end + + class SandboxRequests + UUID: Regexp + USER_OPERATIONS: Array[Symbol] + @client: _SandboxClient + @transport: _SandboxTransport + def initialize: (_SandboxClient client, _SandboxTransport transport) -> void + def call: (SandboxRequest request, ?status: Integer) -> Object? + private def dispatch: (SandboxRequest request, String token) -> Transport::Response + def self.identifier: (Object? value) -> String + def self.request_id: (Hash[Symbol, json_value] options) -> String + def self.selector: (Hash[Symbol, json_value] options) -> Hash[Symbol, json_value] + def self.command: (String command, Hash[Symbol, json_value] options) -> Hash[Symbol, json_value] + end + + module SandboxResponse + STATES: Array[String] + def self.text: (Object? value) -> String + def self.integer: (Object? value) -> Integer + def self.flag: (Object? value) -> bool + def self.state: (Object? value) -> String + def self.command: (Object? value) -> SandboxCommandResult + def self.execution: (Object? value) -> SandboxExecutionResult + def self.access: (Object? value) -> SandboxAccess + def self.preset: (Object? value) -> SandboxPreset + def self.array: (Object? value) -> Array[Object?] + end + + class Sandboxes + @requests: SandboxRequests + def initialize: (_SandboxClient client, _SandboxTransport transport) -> void + end + + class SandboxSession + @requests: SandboxRequests + def initialize: (SandboxRequests requests, Object? value) -> void + private def cleanup: (Exception? original_error) -> (SandboxSession | nil) + private def update: (Symbol operation, ?status: Integer) -> SandboxSession + private def assign_state: (Hash[String, json_value] data) -> void + end + + class SandboxFiles + FILE_LIMIT: Integer + @requests: SandboxRequests + @session_id: String + def initialize: (SandboxRequests requests, String session_id) -> void + private def request: (Symbol operation, Hash[Symbol, json_value] body) -> SandboxRequest + end +end diff --git a/spec/volcano/sandboxes_spec.rb b/spec/volcano/sandboxes_spec.rb new file mode 100644 index 00000000..db9240fa --- /dev/null +++ b/spec/volcano/sandboxes_spec.rb @@ -0,0 +1,269 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'support/session_fixtures' + +RSpec.describe Volcano::Sandboxes do + include SessionFixtures + + def project = '00000000-0000-4000-8000-000000000001' + def session_id = '00000000-0000-4000-8000-000000000002' + def subject_id = '00000000-0000-4000-8000-000000000003' + def request_id = '00000000-0000-4000-8000-000000000004' + let(:client) { Volcano::Client.new(anon_key: 'anon', service_key: 'service', api_url: 'https://sandbox.test') } + let(:facade) { client.sandboxes } + let(:requests) { [] } + let(:command) do + { stdout: 'hello', stderr: 'err', exit_code: 7, + timed_out: false, stdout_truncated: false, stderr_truncated: true } + end + + def state(value = 'running') + { id: session_id, project_id: project, region: 'aws-us-east-1', state: value, expires_at: 'tomorrow' } + end + + def reply(payload = nil, status: 200) + response = Typhoeus::Response.new(code: status, body: JSON.generate(payload), + headers: { 'Content-Type' => 'application/json' }) + Typhoeus.stub(%r{\Ahttps://sandbox.test}).and_return do |request| + requests << request + response + end + end + + after { Typhoeus::Expectation.clear } + + it 'creates a named session without overriding its configured memory' do + reply(state, status: 201) + handle = facade.create(project, region: 'aws-us-east-1', sandbox_id: subject_id, request_id: request_id) + expect(handle.id).to eq(session_id) + expect(handle.project_id).to eq(project) + expect(handle.region).to eq('aws-us-east-1') + expect(handle.expires_at).to eq('tomorrow') + end + + it 'returns nonzero command exits as one-shot data' do + reply(command.merge(session_id: session_id, region: 'aws-us-east-1', duration_ms: 42)) + result = facade.exec(project, 'exit 7', region: 'aws-us-east-1', preset: 'python3.12') + expect(result.exit_code).to eq(7) + expect(result.duration_ms).to eq(42) + expect(result.stderr_truncated).to be(true) + end + + it 'executes in a session with a caller retry identity' do + reply(state) + handle = facade.get(session_id) + reply(command) + expect(handle.exec('exit 7', timeout_seconds: 3600, request_id: request_id).stdout).to eq('hello') + end + + { refresh: ['running', 200], suspend: ['suspending', 202], resume: ['resuming', 202], + terminate: ['terminating', 202] }.each do |operation, (value, status)| + it "updates observed state after #{operation}" do + reply(state) + handle = facade.get(session_id) + reply(state(value), status: status) + expect(handle.public_send(operation)).to be(handle) + expect(handle.state).to eq(value) + end + end + + it 'preserves arbitrary bytes through guest files' do + reply(state) + handle = facade.get(session_id) + bytes = (0..255).to_a.pack('C*') + reply(nil, status: 204) + expect(handle.files.write('/workspace/data', bytes)).to be_nil + reply({ data: Base64.strict_encode64(bytes) }) + expect(handle.files.read('/workspace/data')).to eq(bytes) + end + + it 'refuses oversized files before dispatch' do + reply(state) + handle = facade.get(session_id) + expect { handle.files.write('/workspace/data', 'x' * ((8 * 1024 * 1024) + 1)) } + .to raise_error(Volcano::Error::ValidationError) + end + + it 'redacts expiring access credentials from inspection' do + reply(state) + handle = facade.get(session_id) + reply({ url: 'https://access.test', token: 'secret', expires_at: 'tomorrow' }) + access = handle.access(8080) + expect(access.token).to eq('secret') + expect(access.url).to eq('https://access.test') + expect(access.expires_at).to eq('tomorrow') + expect(access.inspect).not_to include('secret') + end + + it 'grants and revokes session access for a backend-selected user' do + reply(nil, status: 204) + expect(facade.grant(session_id, subject_id, expires_at: 'tomorrow')).to be_nil + expect(facade.revoke(session_id, subject_id)).to be_nil + end + + it 'lists typed presets' do + reply({ data: [{ id: 'python3.12', memory_mb: 2048, regions: ['aws-us-east-1'] }] }) + expect(facade.presets.first).to eq(Volcano::SandboxPreset.new(id: 'python3.12', memory_mb: 2048, + regions: ['aws-us-east-1'])) + end + + [{}, { preset: 'python3.12', sandbox_id: 'invalid' }, { sandbox_id: 'invalid' }].each do |options| + it "refuses an invalid selector #{options}" do + expect { facade.create(project, region: 'aws-us-east-1', **options) }.to raise_error(Volcano::Error::ValidationError) + end + end + + it 'does not use anonymous credentials' do + anonymous = Volcano::Client.new(anon_key: 'anon') + expect { anonymous.sandboxes.get(session_id) }.to raise_error(Volcano::Error::AuthenticationError) + end + + it 'preserves structured conflict errors' do + reply({ error: 'denied', code: 'sandbox_denied' }, status: 409) + expect { facade.get(session_id) }.to raise_error(Volcano::Error::ConflictError) { |error| expect(error.code).to eq('sandbox_denied') } + end + + it 'requests termination when a block raises' do + reply(state) + handle = facade.get(session_id) + reply(state('terminating'), status: 202) + expect { handle.use { raise ArgumentError, 'body failed' } }.to raise_error(ArgumentError, 'body failed') + expect(handle.state).to eq('terminating') + end + + it 'does not terminate an already terminated session' do + reply(state('terminated')) + handle = facade.get(session_id) + expect(handle.use(&:id)).to eq(session_id) + end + + it 'refuses mismatched identity without mutating the handle' do + reply(state) + handle = facade.get(session_id) + reply(state('terminated').merge(id: subject_id)) + expect { handle.refresh }.to raise_error(TypeError, 'Sandbox session identity changed') + expect(handle.state).to eq('running') + end + + [{ stdout: 1 }, { exit_code: true }, { timed_out: 'false' }].each do |invalid| + it "refuses invalid command output #{invalid}" do + reply(command.merge(session_id: session_id, region: 'aws-us-east-1', duration_ms: 42).merge(invalid)) + expect { facade.exec(project, 'run', region: 'aws-us-east-1', preset: 'python3.12') }.to raise_error(TypeError) + end + end + + it 'refuses an unknown session state' do + reply(state('invalid')) + expect { facade.get(session_id) }.to raise_error(TypeError) + end + + it 'refuses a malformed catalog' do + reply({ data: {} }) + expect { facade.presets }.to raise_error(TypeError) + end + + it 'uses the active session credential instead of the service key' do + session = Volcano::Session.new(access_token: 'user-access', refresh_token: 'refresh', user_id: subject_id) + client.auth.current_session = session + reply(state) + facade.get(session_id) + expect(requests.last.options[:headers]['Authorization']).to eq('Bearer user-access') + end + + it 'preserves caller request identities and omits named memory overrides on the wire' do + reply(state, status: 201) + 2.times { facade.create(project, region: 'aws-us-east-1', sandbox_id: subject_id, request_id: request_id) } + expect(requests.map { |request| request.options[:headers][:'Idempotency-Key'] }).to eq([request_id, request_id]) + expect(JSON.parse(requests.last.options[:body])).to eq('region' => 'aws-us-east-1', 'sandbox_id' => subject_id) + expect(requests.last.options[:headers]['Authorization']).to eq('Bearer service') + end + + it 'extends the HTTP timeout to include command completion' do + reply(state) + handle = facade.get(session_id) + reply(command) + handle.exec('run', timeout_seconds: 3600) + expect(requests.last.options[:timeout]).to eq(3_720_000) + end + + it 'does not replay a command after a lost transport response' do + failure = Typhoeus::Response.new(code: 0, return_code: :operation_timedout) + Typhoeus.stub(%r{\Ahttps://sandbox.test}).and_return(failure) + expect { facade.exec(project, 'run', region: 'aws-us-east-1', preset: 'python3.12') } + .to raise_error(Volcano::Error::TransportError) + end + + it 'keeps service credentials for management after signing in' do + client.auth.current_session = Volcano::Session.new(access_token: 'user', refresh_token: 'refresh', + user_id: subject_id) + reply(state, status: 201) + handle = facade.create(project, region: 'aws-us-east-1', preset: 'python3.12') + %i[suspend resume terminate].each do |operation| + reply(state, status: 202) + handle.public_send(operation) + end + reply(nil, status: 204) + facade.grant(session_id, subject_id, expires_at: 'tomorrow') + facade.revoke(session_id, subject_id) + reply(command.merge(session_id: session_id, region: 'aws-us-east-1', duration_ms: 1)) + facade.exec(project, 'run', region: 'aws-us-east-1', preset: 'python3.12') + expect(requests.map { |request| request.options[:headers]['Authorization'] }).to all(eq('Bearer service')) + end + + it 'refreshes a rejected user credential once while preserving the command identity' do + client.auth.current_session = Volcano::Session.new(access_token: access_token, refresh_token: 'refresh', + user_id: subject_id) + reply(state) + handle = facade.get(session_id) + responses = [ + Typhoeus::Response.new(code: 401, body: '{}', headers: {}), + Typhoeus::Response.new(code: 200, headers: {}, + body: JSON.generate(access_token: access_token('new'), + refresh_token: 'new-refresh', token_type: 'bearer', expires_in: 3600, + user: { id: subject_id, email: 'u@example.com', status: 'active' })), + Typhoeus::Response.new(code: 200, headers: {}, body: JSON.generate(command)) + ] + Typhoeus.stub(%r{\Ahttps://sandbox.test}).and_return do |request| + requests << request + responses.shift + end + expect(handle.exec('run', request_id: request_id).stdout).to eq('hello') + expect(responses).to be_empty + expect(requests[-3].options[:headers][:'Idempotency-Key']).to eq(request_id) + expect(requests.last.options[:headers][:'Idempotency-Key']).to eq(request_id) + expect(requests.last.options[:headers]['Authorization']).to eq("Bearer #{access_token('new')}") + end + + it 'preserves a block error when termination also fails' do + reply(state) + handle = facade.get(session_id) + reply({ error: 'cleanup failed' }, status: 500) + expect { handle.use { raise ArgumentError, 'body failed' } }.to raise_error(ArgumentError, 'body failed') + expect(requests.last.url).to end_with("/sandbox-sessions/#{session_id}") + expect(requests.last.options[:method]).to eq(:delete) + end + + it 'reports termination errors when the block succeeds' do + reply(state) + handle = facade.get(session_id) + reply({ error: 'cleanup failed' }, status: 500) + expect { handle.use(&:id) }.to raise_error(Volcano::Error::VolcanoError) + end + + it 'keeps user credentials for all granted session operations' do + client.auth.current_session = Volcano::Session.new(access_token: 'user', refresh_token: 'refresh', + user_id: subject_id) + reply(state) + handle = facade.get(session_id) + reply(command) + handle.exec('run') + reply(nil, status: 204) + handle.files.write('/workspace/file', 'hello') + reply({ data: 'aGVsbG8=' }) + expect(handle.files.read('/workspace/file')).to eq('hello') + reply({ url: 'https://access.test', token: 'secret', expires_at: 'tomorrow' }) + handle.access(8080) + expect(requests.map { |request| request.options[:headers]['Authorization'] }).to all(eq('Bearer user')) + end +end diff --git a/spec/volcano_generation_spec.rb b/spec/volcano_generation_spec.rb index 11317dc3..70d2ed5e 100644 --- a/spec/volcano_generation_spec.rb +++ b/spec/volcano_generation_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Volcano do let(:root) { File.expand_path('..', __dir__) } - let(:openapi_sha256) { 'b5a1dab08ba903ae65d590bcec3601e3b551318dd56637f0d1ed2bdb555e5457' } + let(:openapi_sha256) { '5d4106157fcb559d6fa948154365325b7cfeff2d3196602631b35259de01b6ca' } it 'preserves explicit null without turning omitted object fields into null' do Dir.mktmpdir('volcano-ruby-nullable') do |directory| @@ -63,7 +63,9 @@ 'search_project_logs', 'get_project_log_activity', 'acquire_project_lock', - 'release_project_lock' + 'release_project_lock', + 'create_sandbox_session', + 'execute_sandbox_session' ) model_base = File.binread( File.join(output, 'lib/volcano-generated/api_model_base.rb')